This commit is contained in:
holger.trampe 2021-02-19 12:01:09 +01:00
parent f78a6a68f5
commit 6fcdbce2aa
20 changed files with 162 additions and 34 deletions

View File

@ -17,6 +17,7 @@ class MainStatistic(models.Model):
allfiles = models.IntegerField(default=0) allfiles = models.IntegerField(default=0)
allfiles_storage = models.FloatField(default=0.0) allfiles_storage = models.FloatField(default=0.0)
logins = models.IntegerField(default=0) logins = models.IntegerField(default=0)
mrr = models.FloatField(default=0.0)
class MainSalesMonth(models.Model): class MainSalesMonth(models.Model):
salesmonthdate = models.DateField(default=timezone.now) salesmonthdate = models.DateField(default=timezone.now)

View File

@ -1,6 +1,7 @@
{% extends "adm/adm_base.html" %} {% extends "adm/adm_base.html" %}
{% block content %} {% block content %}
{% load adm_tags %} {% load adm_tags %}
{% load humanize %}
{% load mathfilters %} {% load mathfilters %}
<div class="content-section col-12"> <div class="content-section col-12">
{% if statistik|length > 0 %} {% if statistik|length > 0 %}
@ -15,6 +16,9 @@
<canvas id="all_stats"></canvas> <canvas id="all_stats"></canvas>
</div> </div>
<hr> <hr>
{% getMRR as finalmrr %}
<h5>MRR: {{finalmrr|floatformat:2|intcomma}} €</h5>
<hr>
<h5>Monatliche Umsätze</h5> <h5>Monatliche Umsätze</h5>
<table> <table>
<thead> <thead>
@ -99,6 +103,16 @@ new Chart(document.getElementById("all_stats"),{
"borderColor":"#FA746A", "borderColor":"#FA746A",
"lineTension":0.1 "lineTension":0.1
}, },
{"label":"MRR",
"data":[
{% for ele in statistik %}
{{ele.mrr|stringformat:".2f"}},
{% endfor %}
],
"fill":false,
"borderColor":"#FA746A",
"lineTension":0.1
},
{"label":"Chatnachrichten", {"label":"Chatnachrichten",
"data":[ "data":[
{% for ele in statistik %} {% for ele in statistik %}

View File

@ -70,6 +70,25 @@ def loadBillValue(bill):
return returnvalue return returnvalue
'''
Hier wird der aktuelle Umsatz berechnet anhand Abos und Mitarbeiterzahlen
'''
@register.simple_tag
def getMRR():
# Berechnung das MRR
finalmrr = 0
allag_withabo = Agency.objects.filter(paymentplan=1)
abos = len(allag_withabo) * 21
extrausercount = 0
for ag in allag_withabo:
user_ag = User.objects.filter(profile__agency=ag)
if len(user_ag) > 3:
extrausercount += len(user_ag) - 3
finalmrr = abos + 3*extrausercount
return finalmrr

View File

@ -503,7 +503,19 @@ def statisticCronJob(request, code):
nm = MainSalesMonth(value=monthvalue, salesmonthdate=lastmonth) nm = MainSalesMonth(value=monthvalue, salesmonthdate=lastmonth)
nm.save() nm.save()
newMainS = MainStatistic(agencys=agencycount,users=usercount,standards=standardcount,chatmessages=chatmesscount, active_abos=abocount, absenceobjects=absenceobjects, user_active_timemanagement=user_active_timemanagement, organizerobjects=organizerobjects, agency_recoverobjects=agency_recoverobjects, allfiles=allfiles, allfiles_storage=allfiles_storage, logins=logins)
# Monatlicher MRR
finalrma = 0
allag_withabo = Agency.objects.filter(paymentplan=1)
abos = len(allag_withabo) * 21
extrausercount = 0
for ag in allag_withabo:
user_ag = User.objects.filter(profile__agency=ag)
if len(user_ag) > 3:
extrausercount += len(user_ag) - 3
finalmrr = abos + 3*extrausercount
newMainS = MainStatistic(agencys=agencycount,users=usercount,standards=standardcount,chatmessages=chatmesscount, active_abos=abocount, absenceobjects=absenceobjects, user_active_timemanagement=user_active_timemanagement, organizerobjects=organizerobjects, agency_recoverobjects=agency_recoverobjects, allfiles=allfiles, allfiles_storage=allfiles_storage, logins=logins, mrr=finalmrr)
newMainS.save() newMainS.save()
data.update({"status" : "success"}) data.update({"status" : "success"})
else: else:

View File

@ -22,7 +22,7 @@ class AgencyNetworkForm(forms.ModelForm):
fields = ['name', 'publicjoin'] fields = ['name', 'publicjoin']
labels = { labels = {
"name" : "Name des Agentuverbunds", "name" : "Name des Agenturverbunds",
"publicjoin" : "Beitritt ohne Bestätigung" "publicjoin" : "Beitritt ohne Bestätigung"
} }

BIN
dump.rdb

Binary file not shown.

View File

@ -243,10 +243,12 @@
</div> </div>
</div></div> </div></div>
<p>Wenn ein Standard erstellt/bearbeitet wurde, kann er nur von einer Person mit dem Recht <i>Standards bearbeiten und freischalten</i> veröffentlicht werden.</p>
{% if perms.users.standardgopublic %}
<p>{{ normalForm.public }} Veröffentlichen</p>
{% else %}
<p>Wenn ein Standard erstellt/bearbeitet wurde, kann er nur von einer Person mit dem Recht <i>Standards freischalten</i> veröffentlicht werden.</p>
{% if perms.users.standardmanager %}
{{ normalForm.public }} Veröffentlichen
{% endif %} {% endif %}
<hr> <hr>

View File

@ -137,7 +137,7 @@
<i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i> <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
</a> </a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink"> <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink">
{% if perms.users.standardmanager %} {% if perms.users.standardgopublic %}
{% if standard.public %} {% if standard.public %}
<a class="dropdown-item" href="{% url 'standard-status' standard.pk %}">Veröffentlichung aufheben</a> <a class="dropdown-item" href="{% url 'standard-status' standard.pk %}">Veröffentlichung aufheben</a>
{% else %} {% else %}
@ -148,9 +148,9 @@
{% endif %} {% endif %}
{% if standard.created_standard_by == request.user or perms.users.standardmanager %} {% if standard.created_standard_by == request.user or perms.users.standardmanager %}
<a class="dropdown-item" href="{% url 'standard-add' standard.pk %}">Bearbeiten</a> <a class="dropdown-item" href="{% url 'standard-add' standard.pk %}">Bearbeiten</a>
{% endif %}
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" href="{% url 'standard-delete' standard.pk %}" >Löschen</a> <a class="dropdown-item text-danger" href="{% url 'standard-delete' standard.pk %}" >Löschen</a>
{% endif %}
</div> </div>
</div> </div>
</td> </td>

View File

@ -64,11 +64,25 @@ def getbool():
global groupbool global groupbool
return groupbool return groupbool
# Zähler ungelesener Nachrichten
@register.simple_tag @register.simple_tag
def getmesscounter(user): def getmesscounter(user):
return len(Message.objects.filter(target_user=user)) return len(Message.objects.filter(target_user=user))
# Zähler unveröffentlichter Standards
@register.simple_tag
def getUnpubStandards(user):
unpubstandards = 0
if user.has_perm("users.standardgopublic"):
unpubstandards = len(Standards.objects.filter(agency=user.profile.agency, public=False))
return unpubstandards
@register.simple_tag
def getAbsenceAsks(user):
unconfirmedab = len(Absence.objects.filter(agency=user.profile.agency, confirm_status=1))
return unconfirmedab
# WORKDAY HISTORY # WORKDAY HISTORY
# TASK: Hier flag prüfen, ob der User selbst den Tag bearbeitet hat - aktuell werden nur fremde Änderugnen angezeigt # TASK: Hier flag prüfen, ob der User selbst den Tag bearbeitet hat - aktuell werden nur fremde Änderugnen angezeigt
@register.simple_tag @register.simple_tag
@ -291,6 +305,16 @@ def getifuserdidcomment(standard, user):
return didcomment return didcomment
'''
Hier wird der dynamische Steckbrief generiert. Es wird geprüft, ob Nutzer in Standards verlinkt sind bzw. in Gruppen
representative_group = models.ManyToManyField(AgencyGroup, blank=True, related_name="group_rep")
executor_group = models.ManyToManyField(AgencyGroup, blank=True, related_name="group_ex")
authority_group = models.ManyToManyField(AgencyGroup, blank=True, related_name="group_aut")
'''
@register.simple_tag @register.simple_tag
def isUserInAuth(task, area, user_id): def isUserInAuth(task, area, user_id):
@ -300,10 +324,19 @@ def isUserInAuth(task, area, user_id):
#st_auth = Standards.objects.filter(agency=user.profile.agency, task=task, area=area) #st_auth = Standards.objects.filter(agency=user.profile.agency, task=task, area=area)
st_auth = Standards.objects.filter(agency=user.profile.agency, task=task, area=area, authority__in=[user]) st_auth = Standards.objects.filter(agency=user.profile.agency, task=task, area=area, authority__in=[user])
found = False found = False
user_to_check = User.objects.get(pk=user_id)
if len(st_auth) > 0: if len(st_auth) > 0:
found = True found = True
# Wenn der User noch nicht gefunden wurde, werden die Gruppen der Standards durchgearbeitet
if found == False:
standards_to_check = Standards.objects.filter(agency=user.profile.agency, task=task, area=area)
for s in standards_to_check:
for group in s.authority_group.all():
if user.groups.filter(name = group.group.name).exists():
found = True
return found return found
@register.simple_tag @register.simple_tag
@ -320,6 +353,14 @@ def isUserInEx(task, area, user_id):
if len(st_auth) > 0: if len(st_auth) > 0:
found = True found = True
# Wenn der User noch nicht gefunden wurde, werden die Gruppen der Standards durchgearbeitet
if found == False:
standards_to_check = Standards.objects.filter(agency=user.profile.agency, task=task, area=area)
for s in standards_to_check:
for group in s.executor_group.all():
if user.groups.filter(name = group.group.name).exists():
found = True
return found return found
@ -335,6 +376,14 @@ def isUserInRep(task, area, user_id):
if len(st_auth) > 0: if len(st_auth) > 0:
found = True found = True
# Wenn der User noch nicht gefunden wurde, werden die Gruppen der Standards durchgearbeitet
if found == False:
standards_to_check = Standards.objects.filter(agency=user.profile.agency, task=task, area=area)
for s in standards_to_check:
for group in s.representative_group.all():
if user.groups.filter(name = group.group.name).exists():
found = True
return found return found
# REALTIME # REALTIME
@ -1051,13 +1100,8 @@ def getTrialDays(agency):
return finaldays return finaldays
@register.simple_tag
def getAbsenceLastHistory(absence):
return absence.history.first()

View File

@ -16,6 +16,8 @@ class Workday(models.Model):
end = models.DateTimeField(default=None, null=True, blank=True) end = models.DateTimeField(default=None, null=True, blank=True)
target = models.FloatField(default=8.0) target = models.FloatField(default=8.0)
freefield = models.TextField(max_length=10000, default="", blank=True) freefield = models.TextField(max_length=10000, default="", blank=True)
lastManualChangeUser = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, default=None, related_name="manuelChangeUser")
lastManualChangeDate = models.DateTimeField(default=None, null=True, blank=True)
history = HistoricalRecords() history = HistoricalRecords()
class Breaks(models.Model): class Breaks(models.Model):

View File

@ -9,7 +9,7 @@
<hr> <hr>
{% if user|usergperm:"absencemanager" %} {% if user|usergperm:"timemanager" %}
<div> <div>
<ul class="nav nav-tabs " id="absencetabs" role="tablist" > <ul class="nav nav-tabs " id="absencetabs" role="tablist" >
{% if user.usertime.usetime == True %} {% if user.usertime.usetime == True %}
@ -282,11 +282,14 @@ $('#absencetabs a').on('click', function (e) {
beforeSend: function(){ beforeSend: function(){
$("#confirm-delete_{{workday.pk}}").modal("toggle"); $("#confirm-delete_{{workday.pk}}").modal("toggle");
}, },
success: function( data ) success: function(data)
{ {
$('#confirm-delete_{{workday.pk}}').on('hidden.bs.modal', function (e) { $('#confirm-delete_{{workday.pk}}').on('hidden.bs.modal', function (e) {
$("#errorDelWorkday").modal("toggle"); $("#errorDelWorkday").modal("toggle");
}); });
},
error: function(xhr){
location.href = location.href;
} }
}); });
}); });

View File

@ -44,8 +44,7 @@
{% else %} {% else %}
{% for workday in workdays %} {% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %} {% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{% getWorkDayHistory workday as wd_history %} {% if workday.lastManualChangeUser != None %}
{% if wd_history.history_user != None %}
style="background-color: #fdcc98;" style="background-color: #fdcc98;"
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@ -51,8 +51,7 @@
{% else %} {% else %}
{% for workday in workdays %} {% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %} {% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{% getWorkDayHistory workday as wd_history %} {% if workday.lastManualChangeUser != None %}
{% if wd_history.history_user != None %}
style="background-color: #fdcc98;" style="background-color: #fdcc98;"
{% endif %} {% endif %}
{% endif %} {% endif %}
@ -312,9 +311,8 @@
{% endif %} {% endif %}
{% counterWDUp %} {% counterWDUp %}
{% getWorkDayHistory workday as wd_history %}
{% if wd_history.history_user != None %} {% if workday.lastManualChangeUser != None %}
<a href="#/" onclick="javascript:$('#wd_history_{{workday.pk}}').modal('toggle');"><i class="fas fa-user-edit"></i></a> <a href="#/" onclick="javascript:$('#wd_history_{{workday.pk}}').modal('toggle');"><i class="fas fa-user-edit"></i></a>
<div class="modal fade" id="wd_history_{{workday.pk}}" tabindex="-1" role="dialog" data-backdrop="static"> <div class="modal fade" id="wd_history_{{workday.pk}}" tabindex="-1" role="dialog" data-backdrop="static">
@ -327,7 +325,7 @@
</button> </button>
</div> </div>
<div class="modal-body" style="text-align: left;"> <div class="modal-body" style="text-align: left;">
<p>Geändert am {{wd_history.history_date|date:"d.m.Y, H:i"}} von {{wd_history.history_user.first_name}} {{wd_history.history_user.last_name}}</p> <p>Geändert am {{workday.lastManualChangeDate|date:"d.m.Y, H:i"}} von {{workday.lastManualChangeUser.first_name}} {{workday.lastManualChangeUser.last_name}}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Schließen</button> <button type="button" class="btn btn-primary" data-dismiss="modal">Schließen</button>

View File

@ -38,19 +38,34 @@
{{form.representator|as_crispy_field}} {{form.representator|as_crispy_field}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h5>Begründung</h5> <h5>Begründung</h5>
<p>{{absence.info}}</p> <p>{% if absence.info|length > 0 %}
{{absence.info}}
{% else %}
Keine Begründung
{% endif %}</p>
</div> </div>
</div> </div>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h5>An- oder Ablehnung</h5> <h5>An- oder Ablehnung</h5>
<p>{{absence.confirm_info}}</p> <p> {% if absence.confirm_info|length > 0 %}
{{absence.confirm_info}}
{% else %}
Kein Hinweis
{% endif %}
</p>
</div> </div>
</div> </div>
<hr>
{% getAbsenceLastHistory absence as ab_history %}
{% if ab_history != None %}
<small>Zuletzt geändert am {{ab_history.history_date|date:"d.m.Y, H:i"}} von {{ab_history.history_user.get_full_name}}</small>
{% endif %}
</div> </div>
<div class="col-5 ml-3"> <div class="col-5 ml-3">

View File

@ -288,7 +288,7 @@ def AbsenceManagmenet(request, activemonth=False, activeyear=False):
@login_required @login_required
def TimeManagementTeamSingle(request, pk, activemonth=False, activeyear=False): def TimeManagementTeamSingle(request, pk, activemonth=False, activeyear=False):
if(request.user.has_perm("users.usermanager")): if(request.user.has_perm("users.timemanager")):
# Setzt die Monatsausgabe auf Deutsch # Setzt die Monatsausgabe auf Deutsch
locale.setlocale(locale.LC_TIME, 'de_DE.UTF-8') locale.setlocale(locale.LC_TIME, 'de_DE.UTF-8')
user = User.objects.get(pk=pk) user = User.objects.get(pk=pk)
@ -403,7 +403,7 @@ def TimeManagement(request, activemonth=False, activeyear=False):
"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), "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 "userhasworkdays" : user_has_workdays
} }
if(request.user.has_perm("users.usermanager")): if(request.user.has_perm("users.timemanager")):
context.update({"usersofagencytm" : User.objects.filter(profile__agency=request.user.profile.agency).order_by('last_name')}) context.update({"usersofagencytm" : User.objects.filter(profile__agency=request.user.profile.agency).order_by('last_name')})
return render(request, 'timemanagement/timemanagement_management.html', context) return render(request, 'timemanagement/timemanagement_management.html', context)
@ -427,6 +427,11 @@ def TimeUpdate(request, pk, team=0):
workday.end = end workday.end = end
workday.freefield = form["freefield"].value() workday.freefield = form["freefield"].value()
workday.target = form["target"].value() workday.target = form["target"].value()
# Speichern, das jemand den Arbeitstag bearbeitet hat
workday.lastManualChangeUser = request.user
workday.lastManualChangeDate = datetime.datetime.now()
workday.save() workday.save()
messages.success(request, f'Arbeitstag aktualisiert') messages.success(request, f'Arbeitstag aktualisiert')
if(team == 1): if(team == 1):

View File

@ -490,7 +490,8 @@ class AgencyGroup(models.Model):
('usermanager', 'Mitarbeiter bearbeiten'), ('usermanager', 'Mitarbeiter bearbeiten'),
('groupmanager', 'Gruppen bearbeiten'), ('groupmanager', 'Gruppen bearbeiten'),
('structuremanager', 'Struktur bearbeiten'), ('structuremanager', 'Struktur bearbeiten'),
('standardmanager', 'Standards bearbeiten und freischalten'), ('standardmanager', 'Standards bearbeiten'),
('standardgopublic', 'Standards freischalten'),
('modulenews', 'News bearbeiten und veröffentlichen'), ('modulenews', 'News bearbeiten und veröffentlichen'),
('modulesconfig', 'Module verwalten'), ('modulesconfig', 'Module verwalten'),
('moduleorganizer', 'Organizer bearbeiten'), ('moduleorganizer', 'Organizer bearbeiten'),
@ -498,6 +499,7 @@ class AgencyGroup(models.Model):
('filedirmanager', 'Ordner bearbeiten'), ('filedirmanager', 'Ordner bearbeiten'),
('filesviewer', 'Dateien lesen'), ('filesviewer', 'Dateien lesen'),
('absencemanager', 'Abwesenheiten verwalten'), ('absencemanager', 'Abwesenheiten verwalten'),
('timemanager', 'Zeiterfassung verwalten'),
('recoverdirmanager', 'Notfallhilfe verwalten') ('recoverdirmanager', 'Notfallhilfe verwalten')
] ]

View File

@ -156,6 +156,8 @@
Agentur Agentur
</div> </div>
{% getUnpubStandards request.user as standardUnPubCount %}
{% if active_link == 'standards' %} {% if active_link == 'standards' %}
<li class="nav-item active"> <li class="nav-item active">
{% else%} {% else%}
@ -163,8 +165,11 @@
{%endif%} {%endif%}
<a class="nav-link " href="{% url 'standards' %}" aria-expanded="true"> <a class="nav-link " href="{% url 'standards' %}" aria-expanded="true">
<i class="fas fa-fw fa-lightbulb"></i> <i class="fas fa-fw fa-lightbulb"></i>
<!-- TASK: Anzahl unveröffentlicher Standards als Bubble hier rein --> <span>Standards
<span>Standards</span> {% if standardUnPubCount > 0 %}
<span class="badge badge-primary badge-counter" id="messcounter_badge" style="margin-right: 95px;"><span id="messcounter">{{standardUnPubCount}}</span></span>&nbsp;
{% endif %}
</span>
</a> </a>
</li> </li>
@ -245,6 +250,9 @@
--> -->
{% if request.user.profile.agency.module_timemanagement %} {% if request.user.profile.agency.module_timemanagement %}
{% getAbsenceAsks request.user as ab_ask %}
{% if active_link == 'abscence' %} {% if active_link == 'abscence' %}
<li class="nav-item active"> <li class="nav-item active">
{% else%} {% else%}
@ -253,7 +261,11 @@
<a class="nav-link " href="{% url 'tma-management' %}" aria-expanded="true"> <a class="nav-link " href="{% url 'tma-management' %}" aria-expanded="true">
<i class="fas fa-umbrella-beach"></i> <i class="fas fa-umbrella-beach"></i>
<!-- TASK: Anzahl ausstehender Anträge hier rein --> <!-- TASK: Anzahl ausstehender Anträge hier rein -->
<span>Abwesenheiten&nbsp;<sup>BETA</sup></span> <span>Abwesenheiten&nbsp;
{% if ab_ask > 0 and request.user|usergperm:"absencemanager" %}
<span class="badge badge-primary badge-counter" id="messcounter_badge" style="margin-right: 62px;"><span id="messcounter">{{ab_ask}}</span></span>&nbsp;
{% endif %}
</span>
</a> </a>
</li> </li>
{% endif %} {% endif %}