{% endfor %}
diff --git a/cotisations/templates/cotisations/control.html b/cotisations/templates/cotisations/control.html
index 461494dd..6e9ccbc5 100644
--- a/cotisations/templates/cotisations/control.html
+++ b/cotisations/templates/cotisations/control.html
@@ -42,7 +42,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Profil
{% include "buttons/sort.html" with prefix='control' col='name' text='Nom' %}
{% include "buttons/sort.html" with prefix='control' col='surname' text='Prénom' %}
-
Designation
+
{% include "buttons/sort.html" with prefix='control' col='id' text='Id facture' %}
+
{% include "buttons/sort.html" with prefix='control' col='user-id' text='Id user' %}
+
Designation
Prix total
{% include "buttons/sort.html" with prefix='control' col='paiement' text='Moyen de paiement' %}
{% include "buttons/sort.html" with prefix='control' col='date' text='Date' %}
@@ -58,7 +60,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{{ form.instance.user.name }}
{{ form.instance.user.surname }}
-
{{ form.instance.name }}
+
{{ form.instance.id }}
+
{{ form.instance.user.id }}
+
{{ form.instance.name }}
{{ form.instance.prix_total }}
{{ form.instance.paiement }}
{{ form.instance.date }}
diff --git a/cotisations/views.py b/cotisations/views.py
index 2605e854..4dd6487a 100644
--- a/cotisations/views.py
+++ b/cotisations/views.py
@@ -33,6 +33,7 @@ from django.contrib.auth.decorators import login_required, permission_required
from django.contrib import messages
from django.db.models import ProtectedError
from django.db import transaction
+from django.db.models import Q
from django.forms import modelformset_factory, formset_factory
from django.utils import timezone
from reversion import revisions as reversion
@@ -45,10 +46,21 @@ from re2o.views import form
from re2o.utils import SortTable
from preferences.models import OptionalUser, AssoOption, GeneralOption
from .models import Facture, Article, Vente, Paiement, Banque
-from .forms import NewFactureForm, TrezEditFactureForm, EditFactureForm
-from .forms import ArticleForm, DelArticleForm, PaiementForm, DelPaiementForm
-from .forms import BanqueForm, DelBanqueForm, NewFactureFormPdf
-from .forms import SelectArticleForm, CreditSoldeForm
+from .forms import (
+ NewFactureForm,
+ TrezEditFactureForm,
+ EditFactureForm,
+ ArticleForm,
+ DelArticleForm,
+ PaiementForm,
+ DelPaiementForm,
+ BanqueForm,
+ DelBanqueForm,
+ NewFactureFormPdf,
+ SelectUserArticleForm,
+ SelectClubArticleForm,
+ CreditSoldeForm
+)
from .tex import render_tex
@@ -69,10 +81,15 @@ def new_facture(request, userid):
return redirect(reverse('cotisations:index'))
facture = Facture(user=user)
# Le template a besoin de connaitre les articles pour le js
- article_list = Article.objects.all()
+ article_list = Article.objects.filter(
+ Q(type_user='All') | Q(type_user=request.user.class_name)
+ )
# On envoie la form fature et un formset d'articles
facture_form = NewFactureForm(request.POST or None, instance=facture)
- article_formset = formset_factory(SelectArticleForm)(request.POST or None)
+ if request.user.is_class_club:
+ article_formset = formset_factory(SelectClubArticleForm)(request.POST or None)
+ else:
+ article_formset = formset_factory(SelectUserArticleForm)(request.POST or None)
if facture_form.is_valid() and article_formset.is_valid():
new_facture_instance = facture_form.save(commit=False)
articles = article_formset
@@ -111,7 +128,7 @@ def new_facture(request, userid):
facture=new_facture_instance,
name=article.name,
prix=article.prix,
- iscotisation=article.iscotisation,
+ type_cotisation=article.type_cotisation,
duration=article.duration,
number=quantity
)
@@ -119,7 +136,7 @@ def new_facture(request, userid):
new_vente.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
- if any(art_item.cleaned_data['article'].iscotisation
+ if any(art_item.cleaned_data['article'].type_cotisation
for art_item in articles if art_item.cleaned_data):
messages.success(
request,
@@ -326,8 +343,6 @@ def credit_solde(request, userid):
facture=facture_instance,
name="solde",
prix=facture.cleaned_data['montant'],
- iscotisation=False,
- duration=0,
number=1
)
with transaction.atomic(), reversion.create_revision():
diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py
index 8f2a6f8a..16f3f084 100644
--- a/freeradius_utils/auth.py
+++ b/freeradius_utils/auth.py
@@ -247,6 +247,9 @@ def check_user_machine_and_register(nas_type, username, mac_address):
return (False, u"Machine enregistrée sur le compte d'un autre user...", '')
elif not interface.is_active:
return (False, u"Machine desactivée", '')
+ elif not interface.ipv4:
+ interface.assign_ipv4()
+ return (True, u"Ok, Reassignation de l'ipv4", user.pwd_ntlm)
else:
return (True, u"Access ok", user.pwd_ntlm)
elif nas_type:
@@ -324,9 +327,14 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
return (sw_name, u'Access Ok, Capture de la mac...' + extra_log, DECISION_VLAN)
else:
return (sw_name, u'Erreur dans le register mac %s' % reason + unicode(mac_address), VLAN_NOK)
- elif not interface.first().is_active:
- return (sw_name, u'Machine non active / adherent non cotisant', VLAN_NOK)
else:
- return (sw_name, u'Machine OK' + extra_log, DECISION_VLAN)
+ interface = interface.first()
+ if not interface.is_active:
+ return (sw_name, u'Machine non active / adherent non cotisant', VLAN_NOK)
+ elif not interface.ipv4:
+ interface.assign_ipv4()
+ return (sw_name, u"Ok, Reassignation de l'ipv4" + extra_log, DECISION_VLAN)
+ else:
+ return (sw_name, u'Machine OK' + extra_log, DECISION_VLAN)
diff --git a/logs/views.py b/logs/views.py
index 0fcd6a33..1bd91a97 100644
--- a/logs/views.py
+++ b/logs/views.py
@@ -47,13 +47,50 @@ from django.db.models import Count
from reversion.models import Revision
from reversion.models import Version, ContentType
-from users.models import User, ServiceUser, Right, School, ListRight, ListShell
-from users.models import Ban, Whitelist
-from cotisations.models import Facture, Vente, Article, Banque, Paiement
-from cotisations.models import Cotisation
-from machines.models import Machine, MachineType, IpType, Extension, Interface
-from machines.models import Domain, IpList
-from topologie.models import Switch, Port, Room
+from users.models import (
+ User,
+ ServiceUser,
+ Right,
+ School,
+ ListRight,
+ ListShell,
+ Ban,
+ Whitelist,
+ Adherent,
+ Club
+)
+from cotisations.models import (
+ Facture,
+ Vente,
+ Article,
+ Banque,
+ Paiement,
+ Cotisation
+)
+from machines.models import (
+ Machine,
+ MachineType,
+ IpType,
+ Extension,
+ Interface,
+ Domain,
+ IpList,
+ OuverturePortList,
+ Service,
+ Vlan,
+ Nas,
+ SOA,
+ Mx,
+ Ns
+)
+from topologie.models import (
+ Switch,
+ Port,
+ Room,
+ Stack,
+ ModelSwitch,
+ ConstructorSwitch
+)
from preferences.models import GeneralOption
from re2o.views import form
from re2o.utils import all_whitelisted, all_baned, all_has_access, all_adherent
@@ -184,45 +221,77 @@ def stats_general(request):
range, et les statistiques générales sur les users : users actifs,
cotisants, activés, archivés, etc"""
ip_dict = dict()
- for ip_range in IpType.objects.all():
+ for ip_range in IpType.objects.select_related('vlan').all():
all_ip = IpList.objects.filter(ip_type=ip_range)
used_ip = Interface.objects.filter(ipv4__in=all_ip).count()
active_ip = all_active_assigned_interfaces_count().filter(
ipv4__in=IpList.objects.filter(ip_type=ip_range)
).count()
- ip_dict[ip_range] = [ip_range, all_ip.count(),
+ ip_dict[ip_range] = [ip_range, ip_range.vlan, all_ip.count(),
used_ip, active_ip, all_ip.count()-used_ip]
+ _all_adherent = all_adherent()
+ _all_has_access = all_has_access()
+ _all_baned = all_baned()
+ _all_whitelisted = all_whitelisted()
+ _all_active_interfaces_count = all_active_interfaces_count()
+ _all_active_assigned_interfaces_count = all_active_assigned_interfaces_count()
stats = [
- [["Categorie", "Nombre d'utilisateurs"], {
+ [["Categorie", "Nombre d'utilisateurs (total club et adhérents)", "Nombre d'adhérents", "Nombre de clubs"], {
'active_users': [
"Users actifs",
- User.objects.filter(state=User.STATE_ACTIVE).count()],
- 'inactive_users': [
+ User.objects.filter(state=User.STATE_ACTIVE).count(),
+ Adherent.objects.filter(state=Adherent.STATE_ACTIVE).count(),
+ Club.objects.filter(state=Club.STATE_ACTIVE).count()],
+ 'inactive_users': [
"Users désactivés",
- User.objects.filter(state=User.STATE_DISABLED).count()],
+ User.objects.filter(state=User.STATE_DISABLED).count(),
+ Adherent.objects.filter(state=Adherent.STATE_DISABLED).count(),
+ Club.objects.filter(state=Club.STATE_DISABLED).count()],
'archive_users': [
"Users archivés",
- User.objects.filter(state=User.STATE_ARCHIVE).count()],
+ User.objects.filter(state=User.STATE_ARCHIVE).count(),
+ Adherent.objects.filter(state=Adherent.STATE_ARCHIVE).count(),
+ Club.objects.filter(state=Club.STATE_ARCHIVE).count()],
'adherent_users': [
- "Adhérents à l'association",
- all_adherent().count()],
+ "Cotisant à l'association",
+ _all_adherent.count(),
+ _all_adherent.exclude(adherent__isnull=True).count(),
+ _all_adherent.exclude(club__isnull=True).count()],
'connexion_users': [
"Utilisateurs bénéficiant d'une connexion",
- all_has_access().count()],
+ _all_has_access.count(),
+ _all_has_access.exclude(adherent__isnull=True).count(),
+ _all_has_access.exclude(club__isnull=True).count()],
'ban_users': [
"Utilisateurs bannis",
- all_baned().count()],
+ _all_baned.count(),
+ _all_baned.exclude(adherent__isnull=True).count(),
+ _all_baned.exclude(club__isnull=True).count()],
'whitelisted_user': [
"Utilisateurs bénéficiant d'une connexion gracieuse",
- all_whitelisted().count()],
+ _all_whitelisted.count(),
+ _all_whitelisted.exclude(adherent__isnull=True).count(),
+ _all_whitelisted.exclude(club__isnull=True).count()],
'actives_interfaces': [
"Interfaces actives (ayant accès au reseau)",
- all_active_interfaces_count().count()],
+ _all_active_interfaces_count.count(),
+ _all_active_interfaces_count.exclude(
+ machine__user__adherent__isnull=True
+ ).count(),
+ _all_active_interfaces_count.exclude(
+ machine__user__club__isnull=True
+ ).count()],
'actives_assigned_interfaces': [
"Interfaces actives et assignées ipv4",
- all_active_assigned_interfaces_count().count()]
+ _all_active_assigned_interfaces_count.count(),
+ _all_active_assigned_interfaces_count.exclude(
+ machine__user__adherent__isnull=True
+ ).count(),
+ _all_active_assigned_interfaces_count.exclude(
+ machine__user__club__isnull=True
+ ).count()]
}],
- [["Range d'ip", "Nombre d'ip totales", "Ip assignées",
+ [["Range d'ip", "Vlan", "Nombre d'ip totales", "Ip assignées",
"Ip assignées à une machine active", "Ip non assignées"], ip_dict]
]
return render(request, 'logs/stats_general.html', {'stats_list': stats})
@@ -237,6 +306,8 @@ def stats_models(request):
stats = {
'Users': {
'users': [User.PRETTY_NAME, User.objects.count()],
+ 'adherents': [Adherent.PRETTY_NAME, Adherent.objects.count()],
+ 'clubs': [Club.PRETTY_NAME, Club.objects.count()],
'serviceuser': [ServiceUser.PRETTY_NAME,
ServiceUser.objects.count()],
'right': [Right.PRETTY_NAME, Right.objects.count()],
@@ -263,11 +334,30 @@ def stats_models(request):
'alias': [Domain.PRETTY_NAME,
Domain.objects.exclude(cname=None).count()],
'iplist': [IpList.PRETTY_NAME, IpList.objects.count()],
+ 'service': [Service.PRETTY_NAME, Service.objects.count()],
+ 'ouvertureportlist': [
+ OuverturePortList.PRETTY_NAME,
+ OuverturePortList.objects.count()
+ ],
+ 'vlan': [Vlan.PRETTY_NAME, Vlan.objects.count()],
+ 'SOA': [Mx.PRETTY_NAME, Mx.objects.count()],
+ 'Mx': [Mx.PRETTY_NAME, Mx.objects.count()],
+ 'Ns': [Ns.PRETTY_NAME, Ns.objects.count()],
+ 'nas': [Nas.PRETTY_NAME, Nas.objects.count()],
},
'Topologie': {
'switch': [Switch.PRETTY_NAME, Switch.objects.count()],
'port': [Port.PRETTY_NAME, Port.objects.count()],
'chambre': [Room.PRETTY_NAME, Room.objects.count()],
+ 'stack': [Stack.PRETTY_NAME, Stack.objects.count()],
+ 'modelswitch': [
+ ModelSwitch.PRETTY_NAME,
+ ModelSwitch.objects.count()
+ ],
+ 'constructorswitch': [
+ ConstructorSwitch.PRETTY_NAME,
+ ConstructorSwitch.objects.count()
+ ],
},
'Actions effectuées sur la base':
{
diff --git a/machines/models.py b/machines/models.py
index 102289d5..c2cd2143 100644
--- a/machines/models.py
+++ b/machines/models.py
@@ -460,6 +460,15 @@ class Interface(models.Model):
def clean(self, *args, **kwargs):
""" Formate l'addresse mac en mac_bare (fonction filter_mac)
et assigne une ipv4 dans le bon range si inexistante ou incohérente"""
+ # If type was an invalid value, django won't create an attribute type
+ # but try clean() as we may be able to create it from another value
+ # so even if the error as yet been detected at this point, django
+ # continues because the error might not prevent us from creating the
+ # instance.
+ # But in our case, it's impossible to create a type value so we raise
+ # the error.
+ if not hasattr(self, 'type') :
+ raise ValidationError("Le type d'ip choisi n'est pas valide")
self.filter_macaddress()
self.mac_address = str(EUI(self.mac_address)) or None
if not self.ipv4 or self.type.ip_type != self.ipv4.ip_type:
@@ -626,6 +635,8 @@ class IpList(models.Model):
class Service(models.Model):
""" Definition d'un service (dhcp, dns, etc)"""
+ PRETTY_NAME = "Services à générer (dhcp, dns, etc)"
+
service_type = models.CharField(max_length=255, blank=True, unique=True)
min_time_regen = models.DurationField(
default=timedelta(minutes=1),
@@ -673,6 +684,8 @@ def regen(service):
class Service_link(models.Model):
""" Definition du lien entre serveurs et services"""
+ PRETTY_NAME = "Relation entre service et serveur"
+
service = models.ForeignKey('Service', on_delete=models.CASCADE)
server = models.ForeignKey('Interface', on_delete=models.CASCADE)
last_regen = models.DateTimeField(auto_now_add=True)
@@ -702,6 +715,8 @@ class Service_link(models.Model):
class OuverturePortList(models.Model):
"""Liste des ports ouverts sur une interface."""
+ PRETTY_NAME = "Profil d'ouverture de ports"
+
name = models.CharField(
help_text="Nom de la configuration des ports.",
max_length=255
@@ -748,6 +763,8 @@ class OuverturePort(models.Model):
On limite les ports entre 0 et 65535, tels que défini par la RFC
"""
+ PRETTY_NAME = "Plage de port ouverte"
+
TCP = 'T'
UDP = 'U'
IN = 'I'
diff --git a/re2o/utils.py b/re2o/utils.py
index 2560e6c2..0fa6a84c 100644
--- a/re2o/utils.py
+++ b/re2o/utils.py
@@ -56,6 +56,7 @@ def all_adherent(search_time=DT_NOW):
return User.objects.filter(
facture__in=Facture.objects.filter(
vente__in=Vente.objects.filter(
+ Q(type_cotisation='All') | Q(type_cotisation='Adhesion'),
cotisation__in=Cotisation.objects.filter(
vente__in=Vente.objects.filter(
facture__in=Facture.objects.all().exclude(valid=False)
@@ -94,6 +95,7 @@ def all_has_access(search_time=DT_NOW):
Q(facture__in=Facture.objects.filter(
vente__in=Vente.objects.filter(
cotisation__in=Cotisation.objects.filter(
+ Q(type_cotisation='All') | Q(type_cotisation='Connexion'),
vente__in=Vente.objects.filter(
facture__in=Facture.objects.all()
.exclude(valid=False)
@@ -179,15 +181,18 @@ class SortTable:
'cotis_user': ['user__pseudo'],
'cotis_paiement': ['paiement__moyen'],
'cotis_date': ['date'],
+ 'cotis_id': ['id'],
'default': ['-date']
}
COTISATIONS_CONTROL = {
- 'control_name': ['user__name'],
+ 'control_name': ['user__adherent__name'],
'control_surname': ['user__surname'],
'control_paiement': ['paiement'],
'control_date': ['date'],
'control_valid': ['valid'],
'control_control': ['control'],
+ 'control_id': ['id'],
+ 'control_user-id': ['user__id'],
'default': ['-date']
}
TOPOLOGIE_INDEX = {
diff --git a/templates/base.html b/templates/base.html
index dfaca5eb..d0d5e99a 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -49,6 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+ {% include "cookie_banner.html" %}