mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-04 00:52:49 +00:00
Release : 2.6.1
This commit is contained in:
commit
ef4e430eba
240 changed files with 12666 additions and 3917 deletions
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -120,3 +120,33 @@ Don't forget to run migrations, several settings previously in the `preferences`
|
||||||
in their own Payment models.
|
in their own Payment models.
|
||||||
|
|
||||||
To have a closer look on how the payments works, please go to the wiki.
|
To have a closer look on how the payments works, please go to the wiki.
|
||||||
|
|
||||||
|
## MR 182: Add role models
|
||||||
|
|
||||||
|
Adds the Role model.
|
||||||
|
You need to ensure that your database character set is utf-8.
|
||||||
|
```sql
|
||||||
|
ALTER DATABASE re2o CHARACTER SET utf8;
|
||||||
|
```
|
||||||
|
|
||||||
|
## MR 247: Fix des comptes mails
|
||||||
|
|
||||||
|
Fix several issues with email accounts, you need to collect the static files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./manage.py collectstatic
|
||||||
|
```
|
||||||
|
|
||||||
|
## MR 203 Add custom invoices
|
||||||
|
|
||||||
|
The custom invoices are now stored in database. You need to migrate your database :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 manage.py migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
On some database engines (postgreSQL) you also need to update the id sequences:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 manage.py sqlsequencereset cotisations | python3 manage.py dbshell
|
||||||
|
```
|
||||||
|
|
|
@ -338,6 +338,7 @@ class OptionalMachineSerializer(NamespacedHMSerializer):
|
||||||
class OptionalTopologieSerializer(NamespacedHMSerializer):
|
class OptionalTopologieSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.OptionalTopologie` objects.
|
"""Serialize `preferences.models.OptionalTopologie` objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.OptionalTopologie
|
model = preferences.OptionalTopologie
|
||||||
fields = ('radius_general_policy', 'vlan_decision_ok',
|
fields = ('radius_general_policy', 'vlan_decision_ok',
|
||||||
|
@ -469,10 +470,10 @@ class SwitchPortSerializer(NamespacedHMSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = topologie.Port
|
model = topologie.Port
|
||||||
fields = ('switch', 'port', 'room', 'machine_interface', 'related',
|
fields = ('switch', 'port', 'room', 'machine_interface', 'related',
|
||||||
'radius', 'vlan_force', 'details', 'api_url')
|
'custom_profile', 'state', 'details', 'api_url')
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'related': {'view_name': 'switchport-detail'},
|
'related': {'view_name': 'switchport-detail'},
|
||||||
'api_url': {'view_name': 'switchport-detail'}
|
'api_url': {'view_name': 'switchport-detail'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -484,6 +485,18 @@ class RoomSerializer(NamespacedHMSerializer):
|
||||||
fields = ('name', 'details', 'api_url')
|
fields = ('name', 'details', 'api_url')
|
||||||
|
|
||||||
|
|
||||||
|
class PortProfileSerializer(NamespacedHMSerializer):
|
||||||
|
vlan_untagged = VlanSerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = topologie.PortProfile
|
||||||
|
fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged',
|
||||||
|
'radius_type', 'radius_mode', 'speed', 'mac_limit',
|
||||||
|
'flow_control', 'dhcp_snooping', 'dhcpv6_snooping',
|
||||||
|
'arp_protect', 'ra_guard', 'loop_protect', 'vlan_untagged',
|
||||||
|
'vlan_tagged')
|
||||||
|
|
||||||
|
|
||||||
# USERS
|
# USERS
|
||||||
|
|
||||||
|
|
||||||
|
@ -534,11 +547,20 @@ class AdherentSerializer(NamespacedHMSerializer):
|
||||||
fields = ('name', 'surname', 'pseudo', 'email', 'local_email_redirect',
|
fields = ('name', 'surname', 'pseudo', 'email', 'local_email_redirect',
|
||||||
'local_email_enabled', 'school', 'shell', 'comment',
|
'local_email_enabled', 'school', 'shell', 'comment',
|
||||||
'state', 'registered', 'telephone', 'room', 'solde',
|
'state', 'registered', 'telephone', 'room', 'solde',
|
||||||
'access', 'end_access', 'uid', 'api_url')
|
'access', 'end_access', 'uid', 'api_url','gid')
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'shell': {'view_name': 'shell-detail'}
|
'shell': {'view_name': 'shell-detail'}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HomeCreationSerializer(NamespacedHMSerializer):
|
||||||
|
"""Serialize 'users.models.User' minimal infos to create home
|
||||||
|
"""
|
||||||
|
uid = serializers.IntegerField(source='uid_number')
|
||||||
|
gid = serializers.IntegerField(source='gid_number')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = users.User
|
||||||
|
fields = ('pseudo', 'uid', 'gid')
|
||||||
|
|
||||||
class ServiceUserSerializer(NamespacedHMSerializer):
|
class ServiceUserSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `users.models.ServiceUser` objects.
|
"""Serialize `users.models.ServiceUser` objects.
|
||||||
|
@ -599,7 +621,7 @@ class WhitelistSerializer(NamespacedHMSerializer):
|
||||||
class EMailAddressSerializer(NamespacedHMSerializer):
|
class EMailAddressSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `users.models.EMailAddress` objects.
|
"""Serialize `users.models.EMailAddress` objects.
|
||||||
"""
|
"""
|
||||||
|
user = serializers.CharField(source='user.pseudo', read_only=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = users.EMailAddress
|
model = users.EMailAddress
|
||||||
fields = ('user', 'local_part', 'complete_email_address', 'api_url')
|
fields = ('user', 'local_part', 'complete_email_address', 'api_url')
|
||||||
|
@ -635,9 +657,42 @@ class LocalEmailUsersSerializer(NamespacedHMSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = users.User
|
model = users.User
|
||||||
fields = ('local_email_enabled', 'local_email_redirect',
|
fields = ('local_email_enabled', 'local_email_redirect',
|
||||||
'email_address')
|
'email_address', 'email')
|
||||||
|
|
||||||
|
|
||||||
|
#Firewall
|
||||||
|
|
||||||
|
class FirewallPortListSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = machines.OuverturePort
|
||||||
|
fields = ('begin', 'end', 'protocole', 'io', 'show_port')
|
||||||
|
|
||||||
|
class FirewallOuverturePortListSerializer(serializers.ModelSerializer):
|
||||||
|
tcp_ports_in = FirewallPortListSerializer(many=True, read_only=True)
|
||||||
|
udp_ports_in = FirewallPortListSerializer(many=True, read_only=True)
|
||||||
|
tcp_ports_out = FirewallPortListSerializer(many=True, read_only=True)
|
||||||
|
udp_ports_out = FirewallPortListSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = machines.OuverturePortList
|
||||||
|
fields = ('tcp_ports_in', 'udp_ports_in', 'tcp_ports_out', 'udp_ports_out')
|
||||||
|
|
||||||
|
class SubnetPortsOpenSerializer(serializers.ModelSerializer):
|
||||||
|
ouverture_ports = FirewallOuverturePortListSerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = machines.IpType
|
||||||
|
fields = ('type', 'domaine_ip_start', 'domaine_ip_stop', 'complete_prefixv6', 'ouverture_ports')
|
||||||
|
|
||||||
|
class InterfacePortsOpenSerializer(serializers.ModelSerializer):
|
||||||
|
port_lists = FirewallOuverturePortListSerializer(read_only=True, many=True)
|
||||||
|
ipv4 = serializers.CharField(source='ipv4.ipv4', read_only=True)
|
||||||
|
ipv6 = Ipv6ListSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = machines.Interface
|
||||||
|
fields = ('port_lists', 'ipv4', 'ipv6')
|
||||||
|
|
||||||
# DHCP
|
# DHCP
|
||||||
|
|
||||||
|
|
||||||
|
@ -679,7 +734,7 @@ class NSRecordSerializer(NsSerializer):
|
||||||
"""Serialize `machines.models.Ns` objects with the data needed to
|
"""Serialize `machines.models.Ns` objects with the data needed to
|
||||||
generate a NS DNS record.
|
generate a NS DNS record.
|
||||||
"""
|
"""
|
||||||
target = serializers.CharField(source='ns.name', read_only=True)
|
target = serializers.CharField(source='ns', read_only=True)
|
||||||
|
|
||||||
class Meta(NsSerializer.Meta):
|
class Meta(NsSerializer.Meta):
|
||||||
fields = ('target',)
|
fields = ('target',)
|
||||||
|
@ -689,7 +744,7 @@ class MXRecordSerializer(MxSerializer):
|
||||||
"""Serialize `machines.models.Mx` objects with the data needed to
|
"""Serialize `machines.models.Mx` objects with the data needed to
|
||||||
generate a MX DNS record.
|
generate a MX DNS record.
|
||||||
"""
|
"""
|
||||||
target = serializers.CharField(source='name.name', read_only=True)
|
target = serializers.CharField(source='name', read_only=True)
|
||||||
|
|
||||||
class Meta(MxSerializer.Meta):
|
class Meta(MxSerializer.Meta):
|
||||||
fields = ('target', 'priority')
|
fields = ('target', 'priority')
|
||||||
|
@ -761,13 +816,12 @@ class CNAMERecordSerializer(serializers.ModelSerializer):
|
||||||
"""Serialize `machines.models.Domain` objects with the data needed to
|
"""Serialize `machines.models.Domain` objects with the data needed to
|
||||||
generate a CNAME DNS record.
|
generate a CNAME DNS record.
|
||||||
"""
|
"""
|
||||||
alias = serializers.CharField(source='cname.name', read_only=True)
|
alias = serializers.CharField(source='cname', read_only=True)
|
||||||
hostname = serializers.CharField(source='name', read_only=True)
|
hostname = serializers.CharField(source='name', read_only=True)
|
||||||
extension = serializers.CharField(source='extension.name', read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Domain
|
model = machines.Domain
|
||||||
fields = ('alias', 'hostname', 'extension')
|
fields = ('alias', 'hostname')
|
||||||
|
|
||||||
|
|
||||||
class DNSZonesSerializer(serializers.ModelSerializer):
|
class DNSZonesSerializer(serializers.ModelSerializer):
|
||||||
|
@ -792,6 +846,25 @@ class DNSZonesSerializer(serializers.ModelSerializer):
|
||||||
'aaaa_records', 'cname_records', 'sshfp_records')
|
'aaaa_records', 'cname_records', 'sshfp_records')
|
||||||
|
|
||||||
|
|
||||||
|
class DNSReverseZonesSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serialize the data about DNS Zones.
|
||||||
|
"""
|
||||||
|
soa = SOARecordSerializer(source='extension.soa')
|
||||||
|
extension = serializers.CharField(source='extension.name', read_only=True)
|
||||||
|
cidrs = serializers.ListField(child=serializers.CharField(), source='ip_set_cidrs_as_str', read_only=True)
|
||||||
|
ns_records = NSRecordSerializer(many=True, source='extension.ns_set')
|
||||||
|
mx_records = MXRecordSerializer(many=True, source='extension.mx_set')
|
||||||
|
txt_records = TXTRecordSerializer(many=True, source='extension.txt_set')
|
||||||
|
ptr_records = ARecordSerializer(many=True, source='get_associated_ptr_records')
|
||||||
|
ptr_v6_records = AAAARecordSerializer(many=True, source='get_associated_ptr_v6_records')
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = machines.IpType
|
||||||
|
fields = ('type', 'extension', 'soa', 'ns_records', 'mx_records',
|
||||||
|
'txt_records', 'ptr_records', 'ptr_v6_records', 'cidrs',
|
||||||
|
'prefix_v6', 'prefix_v6_length')
|
||||||
|
|
||||||
# MAILING
|
# MAILING
|
||||||
|
|
||||||
|
|
||||||
|
@ -799,7 +872,7 @@ class MailingMemberSerializer(UserSerializer):
|
||||||
"""Serialize the data about a mailing member.
|
"""Serialize the data about a mailing member.
|
||||||
"""
|
"""
|
||||||
class Meta(UserSerializer.Meta):
|
class Meta(UserSerializer.Meta):
|
||||||
fields = ('name', 'pseudo', 'email')
|
fields = ('name', 'pseudo', 'get_mail')
|
||||||
|
|
||||||
class MailingSerializer(ClubSerializer):
|
class MailingSerializer(ClubSerializer):
|
||||||
"""Serialize the data about a mailing.
|
"""Serialize the data about a mailing.
|
||||||
|
|
|
@ -81,10 +81,12 @@ router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet)
|
||||||
router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet)
|
router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet)
|
||||||
router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet)
|
router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet)
|
||||||
router.register_viewset(r'topologie/building', views.BuildingViewSet)
|
router.register_viewset(r'topologie/building', views.BuildingViewSet)
|
||||||
router.register_viewset(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport')
|
router.register(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport')
|
||||||
router.register_viewset(r'topologie/room', views.RoomViewSet)
|
router.register_viewset(r'topologie/room', views.RoomViewSet)
|
||||||
|
router.register(r'topologie/portprofile', views.PortProfileViewSet)
|
||||||
# USERS
|
# USERS
|
||||||
router.register_viewset(r'users/user', views.UserViewSet)
|
router.register_viewset(r'users/user', views.UserViewSet)
|
||||||
|
router.register_viewset(r'users/homecreation', views.HomeCreationViewSet)
|
||||||
router.register_viewset(r'users/club', views.ClubViewSet)
|
router.register_viewset(r'users/club', views.ClubViewSet)
|
||||||
router.register_viewset(r'users/adherent', views.AdherentViewSet)
|
router.register_viewset(r'users/adherent', views.AdherentViewSet)
|
||||||
router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet)
|
router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet)
|
||||||
|
@ -100,8 +102,12 @@ router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name=
|
||||||
router.register_view(r'dhcp/hostmacip', views.HostMacIpView),
|
router.register_view(r'dhcp/hostmacip', views.HostMacIpView),
|
||||||
# LOCAL EMAILS
|
# LOCAL EMAILS
|
||||||
router.register_view(r'localemail/users', views.LocalEmailUsersView),
|
router.register_view(r'localemail/users', views.LocalEmailUsersView),
|
||||||
|
# Firewall
|
||||||
|
router.register_view(r'firewall/subnet-ports', views.SubnetPortsOpenView),
|
||||||
|
router.register_view(r'firewall/interface-ports', views.InterfacePortsOpenView),
|
||||||
# DNS
|
# DNS
|
||||||
router.register_view(r'dns/zones', views.DNSZonesView),
|
router.register_view(r'dns/zones', views.DNSZonesView),
|
||||||
|
router.register_view(r'dns/reverse-zones', views.DNSReverseZonesView),
|
||||||
# MAILING
|
# MAILING
|
||||||
router.register_view(r'mailing/standard', views.StandardMailingView),
|
router.register_view(r'mailing/standard', views.StandardMailingView),
|
||||||
router.register_view(r'mailing/club', views.ClubMailingView),
|
router.register_view(r'mailing/club', views.ClubMailingView),
|
||||||
|
|
30
api/views.py
30
api/views.py
|
@ -403,6 +403,12 @@ class RoomViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
serializer_class = serializers.RoomSerializer
|
serializer_class = serializers.RoomSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""Exposes list and details of `topologie.models.PortProfile` objects.
|
||||||
|
"""
|
||||||
|
queryset = topologie.PortProfile.objects.all()
|
||||||
|
serializer_class = serializers.PortProfileSerializer
|
||||||
|
|
||||||
# USER
|
# USER
|
||||||
|
|
||||||
|
|
||||||
|
@ -412,6 +418,11 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
queryset = users.User.objects.all()
|
queryset = users.User.objects.all()
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
|
|
||||||
|
class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""Exposes infos of `users.models.Users` objects to create homes.
|
||||||
|
"""
|
||||||
|
queryset = users.User.objects.all()
|
||||||
|
serializer_class = serializers.HomeCreationSerializer
|
||||||
|
|
||||||
class ClubViewSet(viewsets.ReadOnlyModelViewSet):
|
class ClubViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
"""Exposes list and details of `users.models.Club` objects.
|
"""Exposes list and details of `users.models.Club` objects.
|
||||||
|
@ -532,6 +543,16 @@ class HostMacIpView(generics.ListAPIView):
|
||||||
serializer_class = serializers.HostMacIpSerializer
|
serializer_class = serializers.HostMacIpSerializer
|
||||||
|
|
||||||
|
|
||||||
|
#Firewall
|
||||||
|
|
||||||
|
class SubnetPortsOpenView(generics.ListAPIView):
|
||||||
|
queryset = machines.IpType.objects.all()
|
||||||
|
serializer_class = serializers.SubnetPortsOpenSerializer
|
||||||
|
|
||||||
|
class InterfacePortsOpenView(generics.ListAPIView):
|
||||||
|
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
|
||||||
|
serializer_class = serializers.InterfacePortsOpenSerializer
|
||||||
|
|
||||||
# DNS
|
# DNS
|
||||||
|
|
||||||
|
|
||||||
|
@ -549,6 +570,15 @@ class DNSZonesView(generics.ListAPIView):
|
||||||
.all())
|
.all())
|
||||||
serializer_class = serializers.DNSZonesSerializer
|
serializer_class = serializers.DNSZonesSerializer
|
||||||
|
|
||||||
|
class DNSReverseZonesView(generics.ListAPIView):
|
||||||
|
"""Exposes the detailed information about each extension (hostnames,
|
||||||
|
IPs, DNS records, etc.) in order to build the DNS zone files.
|
||||||
|
"""
|
||||||
|
queryset = (machines.IpType.objects.all())
|
||||||
|
serializer_class = serializers.DNSReverseZonesSerializer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# MAILING
|
# MAILING
|
||||||
|
|
||||||
|
|
|
@ -42,4 +42,5 @@ def can_view(user):
|
||||||
if can:
|
if can:
|
||||||
return can, None
|
return can, None
|
||||||
else:
|
else:
|
||||||
return can, _("You don't have the rights to see this application.")
|
return can, _("You don't have the right to view this application.")
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
||||||
|
from .models import CustomInvoice
|
||||||
|
|
||||||
|
|
||||||
class FactureAdmin(VersionAdmin):
|
class FactureAdmin(VersionAdmin):
|
||||||
|
@ -37,6 +38,11 @@ class FactureAdmin(VersionAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CustomInvoiceAdmin(VersionAdmin):
|
||||||
|
"""Admin class for custom invoices."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class VenteAdmin(VersionAdmin):
|
class VenteAdmin(VersionAdmin):
|
||||||
"""Class admin d'une vente, tous les champs (facture related)"""
|
"""Class admin d'une vente, tous les champs (facture related)"""
|
||||||
pass
|
pass
|
||||||
|
@ -69,3 +75,4 @@ admin.site.register(Banque, BanqueAdmin)
|
||||||
admin.site.register(Paiement, PaiementAdmin)
|
admin.site.register(Paiement, PaiementAdmin)
|
||||||
admin.site.register(Vente, VenteAdmin)
|
admin.site.register(Vente, VenteAdmin)
|
||||||
admin.site.register(Cotisation, CotisationAdmin)
|
admin.site.register(Cotisation, CotisationAdmin)
|
||||||
|
admin.site.register(CustomInvoice, CustomInvoiceAdmin)
|
||||||
|
|
|
@ -40,13 +40,13 @@ from django import forms
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import ModelForm, Form
|
from django.forms import ModelForm, Form
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from .models import Article, Paiement, Facture, Banque
|
from .models import Article, Paiement, Facture, Banque, CustomInvoice
|
||||||
from .payment_methods import balance
|
from .payment_methods import balance
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,71 +84,36 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class SelectUserArticleForm(FormRevMixin, Form):
|
class SelectArticleForm(FormRevMixin, Form):
|
||||||
"""
|
"""
|
||||||
Form used to select an article during the creation of an invoice for a
|
Form used to select an article during the creation of an invoice for a
|
||||||
member.
|
member.
|
||||||
"""
|
"""
|
||||||
article = forms.ModelChoiceField(
|
article = forms.ModelChoiceField(
|
||||||
queryset=Article.objects.filter(
|
queryset=Article.objects.none(),
|
||||||
Q(type_user='All') | Q(type_user='Adherent')
|
label=_("Article"),
|
||||||
),
|
|
||||||
label=_l("Article"),
|
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
quantity = forms.IntegerField(
|
quantity = forms.IntegerField(
|
||||||
label=_l("Quantity"),
|
label=_("Quantity"),
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
user = kwargs.pop('user')
|
user = kwargs.pop('user')
|
||||||
super(SelectUserArticleForm, self).__init__(*args, **kwargs)
|
target_user = kwargs.pop('target_user')
|
||||||
self.fields['article'].queryset = Article.find_allowed_articles(user)
|
super(SelectArticleForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['article'].queryset = Article.find_allowed_articles(user, target_user)
|
||||||
|
|
||||||
|
|
||||||
class SelectClubArticleForm(Form):
|
class CustomInvoiceForm(FormRevMixin, ModelForm):
|
||||||
"""
|
"""
|
||||||
Form used to select an article during the creation of an invoice for a
|
Form used to create a custom invoice.
|
||||||
club.
|
|
||||||
"""
|
"""
|
||||||
article = forms.ModelChoiceField(
|
class Meta:
|
||||||
queryset=Article.objects.filter(
|
model = CustomInvoice
|
||||||
Q(type_user='All') | Q(type_user='Club')
|
fields = '__all__'
|
||||||
),
|
|
||||||
label=_l("Article"),
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
quantity = forms.IntegerField(
|
|
||||||
label=_l("Quantity"),
|
|
||||||
validators=[MinValueValidator(1)],
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, user, *args, **kwargs):
|
|
||||||
super(SelectClubArticleForm, self).__init__(*args, **kwargs)
|
|
||||||
self.fields['article'].queryset = Article.find_allowed_articles(user)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO : change Facture to Invoice
|
|
||||||
class NewFactureFormPdf(Form):
|
|
||||||
"""
|
|
||||||
Form used to create a custom PDF invoice.
|
|
||||||
"""
|
|
||||||
paid = forms.BooleanField(label=_l("Paid"), required=False)
|
|
||||||
# TODO : change dest field to recipient
|
|
||||||
dest = forms.CharField(
|
|
||||||
required=True,
|
|
||||||
max_length=255,
|
|
||||||
label=_l("Recipient")
|
|
||||||
)
|
|
||||||
# TODO : change chambre field to address
|
|
||||||
chambre = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
max_length=10,
|
|
||||||
label=_l("Address")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ArticleForm(FormRevMixin, ModelForm):
|
class ArticleForm(FormRevMixin, ModelForm):
|
||||||
|
@ -172,7 +137,7 @@ class DelArticleForm(FormRevMixin, Form):
|
||||||
"""
|
"""
|
||||||
articles = forms.ModelMultipleChoiceField(
|
articles = forms.ModelMultipleChoiceField(
|
||||||
queryset=Article.objects.none(),
|
queryset=Article.objects.none(),
|
||||||
label=_l("Existing articles"),
|
label=_("Available articles"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -212,7 +177,7 @@ class DelPaiementForm(FormRevMixin, Form):
|
||||||
# TODO : change paiement to payment
|
# TODO : change paiement to payment
|
||||||
paiements = forms.ModelMultipleChoiceField(
|
paiements = forms.ModelMultipleChoiceField(
|
||||||
queryset=Paiement.objects.none(),
|
queryset=Paiement.objects.none(),
|
||||||
label=_l("Existing payment method"),
|
label=_("Available payment methods"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -250,7 +215,7 @@ class DelBanqueForm(FormRevMixin, Form):
|
||||||
# TODO : change banque to bank
|
# TODO : change banque to bank
|
||||||
banques = forms.ModelMultipleChoiceField(
|
banques = forms.ModelMultipleChoiceField(
|
||||||
queryset=Banque.objects.none(),
|
queryset=Banque.objects.none(),
|
||||||
label=_l("Existing banks"),
|
label=_("Available banks"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -269,21 +234,21 @@ class RechargeForm(FormRevMixin, Form):
|
||||||
Form used to refill a user's balance
|
Form used to refill a user's balance
|
||||||
"""
|
"""
|
||||||
value = forms.FloatField(
|
value = forms.FloatField(
|
||||||
label=_l("Amount"),
|
label=_("Amount"),
|
||||||
min_value=0.01,
|
min_value=0.01,
|
||||||
validators=[]
|
validators=[]
|
||||||
)
|
)
|
||||||
payment = forms.ModelChoiceField(
|
payment = forms.ModelChoiceField(
|
||||||
queryset=Paiement.objects.none(),
|
queryset=Paiement.objects.none(),
|
||||||
label=_l("Payment method")
|
label=_("Payment method")
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, user=None, **kwargs):
|
def __init__(self, *args, user=None, user_source=None, **kwargs):
|
||||||
self.user = user
|
self.user = user
|
||||||
super(RechargeForm, self).__init__(*args, **kwargs)
|
super(RechargeForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['payment'].empty_label = \
|
self.fields['payment'].empty_label = \
|
||||||
_("Select a payment method")
|
_("Select a payment method")
|
||||||
self.fields['payment'].queryset = Paiement.find_allowed_payments(user)
|
self.fields['payment'].queryset = Paiement.find_allowed_payments(user_source).exclude(is_balance=True)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
|
@ -301,3 +266,4 @@ class RechargeForm(FormRevMixin, Form):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
20
cotisations/migrations/0031_comnpaypayment_production.py
Normal file
20
cotisations/migrations/0031_comnpaypayment_production.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-11 23:03
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cotisations', '0030_custom_payment'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comnpaypayment',
|
||||||
|
name='production',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='Production mode enabled (production url, instead of homologation)'),
|
||||||
|
),
|
||||||
|
]
|
104
cotisations/migrations/0032_custom_invoice.py
Normal file
104
cotisations/migrations/0032_custom_invoice.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-07-21 20:01
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.contrib.auth.management import create_permissions
|
||||||
|
import re2o.field_permissions
|
||||||
|
import re2o.mixins
|
||||||
|
|
||||||
|
|
||||||
|
def reattribute_ids(apps, schema_editor):
|
||||||
|
Facture = apps.get_model('cotisations', 'Facture')
|
||||||
|
BaseInvoice = apps.get_model('cotisations', 'BaseInvoice')
|
||||||
|
|
||||||
|
for f in Facture.objects.all():
|
||||||
|
base = BaseInvoice.objects.create(id=f.pk)
|
||||||
|
base.date = f.date
|
||||||
|
base.save()
|
||||||
|
f.baseinvoice_ptr = base
|
||||||
|
f.save()
|
||||||
|
|
||||||
|
|
||||||
|
def update_rights(apps, schema_editor):
|
||||||
|
Permission = apps.get_model('auth', 'Permission')
|
||||||
|
|
||||||
|
# creates needed permissions
|
||||||
|
app = apps.get_app_config('cotisations')
|
||||||
|
app.models_module = True
|
||||||
|
create_permissions(app)
|
||||||
|
app.models_module = False
|
||||||
|
|
||||||
|
former = Permission.objects.get(codename='change_facture_pdf')
|
||||||
|
new_1 = Permission.objects.get(codename='add_custominvoice')
|
||||||
|
new_2 = Permission.objects.get(codename='change_custominvoice')
|
||||||
|
new_3 = Permission.objects.get(codename='view_custominvoice')
|
||||||
|
new_4 = Permission.objects.get(codename='delete_custominvoice')
|
||||||
|
for group in former.group_set.all():
|
||||||
|
group.permissions.remove(former)
|
||||||
|
group.permissions.add(new_1)
|
||||||
|
group.permissions.add(new_2)
|
||||||
|
group.permissions.add(new_3)
|
||||||
|
group.permissions.add(new_4)
|
||||||
|
group.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cotisations', '0031_comnpaypayment_production'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='BaseInvoice',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
|
||||||
|
],
|
||||||
|
bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, re2o.field_permissions.FieldPermissionModelMixin, models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomInvoice',
|
||||||
|
fields=[
|
||||||
|
('baseinvoice_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice')),
|
||||||
|
('recipient', models.CharField(max_length=255, verbose_name='Recipient')),
|
||||||
|
('payment', models.CharField(max_length=255, verbose_name='Payment type')),
|
||||||
|
('address', models.CharField(max_length=255, verbose_name='Address')),
|
||||||
|
('paid', models.BooleanField(verbose_name='Paid')),
|
||||||
|
],
|
||||||
|
bases=('cotisations.baseinvoice',),
|
||||||
|
options={'permissions': (('view_custominvoice', 'Can view a custom invoice'),)},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='facture',
|
||||||
|
name='baseinvoice_ptr',
|
||||||
|
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', null=True),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.RunPython(reattribute_ids),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='facture',
|
||||||
|
field=models.ForeignKey(on_delete=models.CASCADE, verbose_name='Invoice', to='cotisations.BaseInvoice')
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='facture',
|
||||||
|
name='id',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='facture',
|
||||||
|
name='date',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='facture',
|
||||||
|
name='baseinvoice_ptr',
|
||||||
|
field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(update_rights),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='facture',
|
||||||
|
options={'permissions': (('change_facture_control', 'Can change the "controlled" state'), ('view_facture', "Can see an invoice's details"), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'Invoice', 'verbose_name_plural': 'Invoices'},
|
||||||
|
),
|
||||||
|
]
|
181
cotisations/migrations/0033_auto_20180818_1319.py
Normal file
181
cotisations/migrations/0033_auto_20180818_1319.py
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-18 11:19
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import cotisations.validators
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import re2o.aes_field
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cotisations', '0032_custom_invoice'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='article',
|
||||||
|
options={'permissions': (('view_article', 'Can view an article object'), ('buy_every_article', 'Can buy every article')), 'verbose_name': 'article', 'verbose_name_plural': 'articles'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='balancepayment',
|
||||||
|
options={'verbose_name': 'user balance'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='banque',
|
||||||
|
options={'permissions': (('view_banque', 'Can view a bank object'),), 'verbose_name': 'bank', 'verbose_name_plural': 'banks'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='cotisation',
|
||||||
|
options={'permissions': (('view_cotisation', 'Can view a subscription object'), ('change_all_cotisation', 'Can edit the previous subscriptions')), 'verbose_name': 'subscription', 'verbose_name_plural': 'subscriptions'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='custominvoice',
|
||||||
|
options={'permissions': (('view_custominvoice', 'Can view a custom invoice object'),)},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='facture',
|
||||||
|
options={'permissions': (('change_facture_control', 'Can edit the "controlled" state'), ('view_facture', 'Can view an invoice object'), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'invoice', 'verbose_name_plural': 'invoices'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='paiement',
|
||||||
|
options={'permissions': (('view_paiement', 'Can view a payment method object'), ('use_every_payment', 'Can use every payment method')), 'verbose_name': 'payment method', 'verbose_name_plural': 'payment methods'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='vente',
|
||||||
|
options={'permissions': (('view_vente', 'Can view a purchase object'), ('change_all_vente', 'Can edit all the previous purchases')), 'verbose_name': 'purchase', 'verbose_name_plural': 'purchases'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='available_for_everyone',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='is available for every user'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='duration',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration (in months)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='designation'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='prix',
|
||||||
|
field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='unit price'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='type_cotisation',
|
||||||
|
field=models.CharField(blank=True, choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default=None, max_length=255, null=True, verbose_name='subscription type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='article',
|
||||||
|
name='type_user',
|
||||||
|
field=models.CharField(choices=[('Adherent', 'Member'), ('Club', 'Club'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='type of users concerned'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='banque',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='comnpaypayment',
|
||||||
|
name='payment_credential',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=255, verbose_name='ComNpay VAT Number'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='comnpaypayment',
|
||||||
|
name='payment_pass',
|
||||||
|
field=re2o.aes_field.AESEncryptedField(blank=True, max_length=255, null=True, verbose_name='ComNpay secret key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='comnpaypayment',
|
||||||
|
name='production',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='Production mode enabled (production URL, instead of homologation)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='cotisation',
|
||||||
|
name='date_end',
|
||||||
|
field=models.DateTimeField(verbose_name='end date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='cotisation',
|
||||||
|
name='date_start',
|
||||||
|
field=models.DateTimeField(verbose_name='start date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='cotisation',
|
||||||
|
name='type_cotisation',
|
||||||
|
field=models.CharField(choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='subscription type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='cotisation',
|
||||||
|
name='vente',
|
||||||
|
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='purchase'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='facture',
|
||||||
|
name='cheque',
|
||||||
|
field=models.CharField(blank=True, max_length=255, verbose_name='cheque number'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='facture',
|
||||||
|
name='control',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='controlled'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='facture',
|
||||||
|
name='valid',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='validated'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='paiement',
|
||||||
|
name='available_for_everyone',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='is available for every user'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='paiement',
|
||||||
|
name='is_balance',
|
||||||
|
field=models.BooleanField(default=False, editable=False, help_text='There should be only one balance payment method.', validators=[cotisations.validators.check_no_balance], verbose_name='is user balance'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='paiement',
|
||||||
|
name='moyen',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='method'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='duration',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration (in months)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='facture',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', verbose_name='invoice'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='article'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='number',
|
||||||
|
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='amount'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='prix',
|
||||||
|
field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='price'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vente',
|
||||||
|
name='type_cotisation',
|
||||||
|
field=models.CharField(blank=True, choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], max_length=255, null=True, verbose_name='subscription type'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -41,8 +41,7 @@ from django.dispatch import receiver
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
@ -55,80 +54,11 @@ from cotisations.utils import find_payment_method
|
||||||
from cotisations.validators import check_no_balance
|
from cotisations.validators import check_no_balance
|
||||||
|
|
||||||
|
|
||||||
# TODO : change facture to invoice
|
class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
|
||||||
"""
|
|
||||||
The model for an invoice. It reprensents the fact that a user paid for
|
|
||||||
something (it can be multiple article paid at once).
|
|
||||||
|
|
||||||
An invoice is linked to :
|
|
||||||
* one or more purchases (one for each article sold that time)
|
|
||||||
* a user (the one who bought those articles)
|
|
||||||
* a payment method (the one used by the user)
|
|
||||||
* (if applicable) a bank
|
|
||||||
* (if applicable) a cheque number.
|
|
||||||
Every invoice is dated throught the 'date' value.
|
|
||||||
An invoice has a 'controlled' value (default : False) which means that
|
|
||||||
someone with high enough rights has controlled that invoice and taken it
|
|
||||||
into account. It also has a 'valid' value (default : True) which means
|
|
||||||
that someone with high enough rights has decided that this invoice was not
|
|
||||||
valid (thus it's like the user never paid for his articles). It may be
|
|
||||||
necessary in case of non-payment.
|
|
||||||
"""
|
|
||||||
|
|
||||||
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
|
|
||||||
# TODO : change paiement to payment
|
|
||||||
paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT)
|
|
||||||
# TODO : change banque to bank
|
|
||||||
banque = models.ForeignKey(
|
|
||||||
'Banque',
|
|
||||||
on_delete=models.PROTECT,
|
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
# TODO : maybe change to cheque nummber because not evident
|
|
||||||
cheque = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
blank=True,
|
|
||||||
verbose_name=_l("Cheque number")
|
|
||||||
)
|
|
||||||
date = models.DateTimeField(
|
date = models.DateTimeField(
|
||||||
auto_now_add=True,
|
auto_now_add=True,
|
||||||
verbose_name=_l("Date")
|
verbose_name=_("Date")
|
||||||
)
|
)
|
||||||
# TODO : change name to validity for clarity
|
|
||||||
valid = models.BooleanField(
|
|
||||||
default=True,
|
|
||||||
verbose_name=_l("Validated")
|
|
||||||
)
|
|
||||||
# TODO : changed name to controlled for clarity
|
|
||||||
control = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
verbose_name=_l("Controlled")
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = False
|
|
||||||
permissions = (
|
|
||||||
# TODO : change facture to invoice
|
|
||||||
('change_facture_control',
|
|
||||||
_l("Can change the \"controlled\" state")),
|
|
||||||
# TODO : seems more likely to be call create_facture_pdf
|
|
||||||
# or create_invoice_pdf
|
|
||||||
('change_facture_pdf',
|
|
||||||
_l("Can create a custom PDF invoice")),
|
|
||||||
('view_facture',
|
|
||||||
_l("Can see an invoice's details")),
|
|
||||||
('change_all_facture',
|
|
||||||
_l("Can edit all the previous invoices")),
|
|
||||||
)
|
|
||||||
verbose_name = _l("Invoice")
|
|
||||||
verbose_name_plural = _l("Invoices")
|
|
||||||
|
|
||||||
def linked_objects(self):
|
|
||||||
"""Return linked objects : machine and domain.
|
|
||||||
Usefull in history display"""
|
|
||||||
return self.vente_set.all()
|
|
||||||
|
|
||||||
# TODO : change prix to price
|
# TODO : change prix to price
|
||||||
def prix(self):
|
def prix(self):
|
||||||
|
@ -167,6 +97,74 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
).values_list('name', flat=True))
|
).values_list('name', flat=True))
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
# TODO : change facture to invoice
|
||||||
|
class Facture(BaseInvoice):
|
||||||
|
"""
|
||||||
|
The model for an invoice. It reprensents the fact that a user paid for
|
||||||
|
something (it can be multiple article paid at once).
|
||||||
|
|
||||||
|
An invoice is linked to :
|
||||||
|
* one or more purchases (one for each article sold that time)
|
||||||
|
* a user (the one who bought those articles)
|
||||||
|
* a payment method (the one used by the user)
|
||||||
|
* (if applicable) a bank
|
||||||
|
* (if applicable) a cheque number.
|
||||||
|
Every invoice is dated throught the 'date' value.
|
||||||
|
An invoice has a 'controlled' value (default : False) which means that
|
||||||
|
someone with high enough rights has controlled that invoice and taken it
|
||||||
|
into account. It also has a 'valid' value (default : True) which means
|
||||||
|
that someone with high enough rights has decided that this invoice was not
|
||||||
|
valid (thus it's like the user never paid for his articles). It may be
|
||||||
|
necessary in case of non-payment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
|
||||||
|
# TODO : change paiement to payment
|
||||||
|
paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT)
|
||||||
|
# TODO : change banque to bank
|
||||||
|
banque = models.ForeignKey(
|
||||||
|
'Banque',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
# TODO : maybe change to cheque nummber because not evident
|
||||||
|
cheque = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("cheque number")
|
||||||
|
)
|
||||||
|
# TODO : change name to validity for clarity
|
||||||
|
valid = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name=_("validated")
|
||||||
|
)
|
||||||
|
# TODO : changed name to controlled for clarity
|
||||||
|
control = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("controlled")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = False
|
||||||
|
permissions = (
|
||||||
|
# TODO : change facture to invoice
|
||||||
|
('change_facture_control',
|
||||||
|
_("Can edit the \"controlled\" state")),
|
||||||
|
('view_facture',
|
||||||
|
_("Can view an invoice object")),
|
||||||
|
('change_all_facture',
|
||||||
|
_("Can edit all the previous invoices")),
|
||||||
|
)
|
||||||
|
verbose_name = _("invoice")
|
||||||
|
verbose_name_plural = _("invoices")
|
||||||
|
|
||||||
|
def linked_objects(self):
|
||||||
|
"""Return linked objects : machine and domain.
|
||||||
|
Usefull in history display"""
|
||||||
|
return self.vente_set.all()
|
||||||
|
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *args, **kwargs):
|
||||||
if not user_request.has_perm('cotisations.change_facture'):
|
if not user_request.has_perm('cotisations.change_facture'):
|
||||||
return False, _("You don't have the right to edit an invoice.")
|
return False, _("You don't have the right to edit an invoice.")
|
||||||
|
@ -196,7 +194,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
def can_view(self, user_request, *_args, **_kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.view_facture') and \
|
if not user_request.has_perm('cotisations.view_facture') and \
|
||||||
self.user != user_request:
|
self.user != user_request:
|
||||||
return False, _("You don't have the right to see someone else's "
|
return False, _("You don't have the right to view someone else's "
|
||||||
"invoices history.")
|
"invoices history.")
|
||||||
elif not self.valid:
|
elif not self.valid:
|
||||||
return False, _("The invoice has been invalidated.")
|
return False, _("The invoice has been invalidated.")
|
||||||
|
@ -212,14 +210,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
_("You don't have the right to edit the \"controlled\" state.")
|
_("You don't have the right to edit the \"controlled\" state.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def can_change_pdf(user_request, *_args, **_kwargs):
|
|
||||||
""" Returns True if the user can change this invoice """
|
|
||||||
return (
|
|
||||||
user_request.has_perm('cotisations.change_facture_pdf'),
|
|
||||||
_("You don't have the right to edit an invoice.")
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_create(user_request, *_args, **_kwargs):
|
def can_create(user_request, *_args, **_kwargs):
|
||||||
"""Check if a user can create an invoice.
|
"""Check if a user can create an invoice.
|
||||||
|
@ -231,8 +221,8 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
if user_request.has_perm('cotisations.add_facture'):
|
if user_request.has_perm('cotisations.add_facture'):
|
||||||
return True, None
|
return True, None
|
||||||
if len(Paiement.find_allowed_payments(user_request)) <= 0:
|
if len(Paiement.find_allowed_payments(user_request)) <= 0:
|
||||||
return False, _("There are no payment types which you can use.")
|
return False, _("There are no payment method which you can use.")
|
||||||
if len(Article.find_allowed_articles(user_request)) <= 0:
|
if len(Article.find_allowed_articles(user_request, user_request)) <= 0:
|
||||||
return False, _("There are no article that you can buy.")
|
return False, _("There are no article that you can buy.")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -265,6 +255,28 @@ def facture_post_delete(**kwargs):
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomInvoice(BaseInvoice):
|
||||||
|
class Meta:
|
||||||
|
permissions = (
|
||||||
|
('view_custominvoice', _("Can view a custom invoice object")),
|
||||||
|
)
|
||||||
|
recipient = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("Recipient")
|
||||||
|
)
|
||||||
|
payment = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("Payment type")
|
||||||
|
)
|
||||||
|
address = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("Address")
|
||||||
|
)
|
||||||
|
paid = models.BooleanField(
|
||||||
|
verbose_name=_("Paid")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO : change Vente to Purchase
|
# TODO : change Vente to Purchase
|
||||||
class Vente(RevMixin, AclMixin, models.Model):
|
class Vente(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -281,38 +293,38 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
# TODO : change this to English
|
# TODO : change this to English
|
||||||
COTISATION_TYPE = (
|
COTISATION_TYPE = (
|
||||||
('Connexion', _l("Connexion")),
|
('Connexion', _("Connection")),
|
||||||
('Adhesion', _l("Membership")),
|
('Adhesion', _("Membership")),
|
||||||
('All', _l("Both of them")),
|
('All', _("Both of them")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO : change facture to invoice
|
# TODO : change facture to invoice
|
||||||
facture = models.ForeignKey(
|
facture = models.ForeignKey(
|
||||||
'Facture',
|
'BaseInvoice',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name=_l("Invoice")
|
verbose_name=_("invoice")
|
||||||
)
|
)
|
||||||
# TODO : change number to amount for clarity
|
# TODO : change number to amount for clarity
|
||||||
number = models.IntegerField(
|
number = models.IntegerField(
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
verbose_name=_l("Amount")
|
verbose_name=_("amount")
|
||||||
)
|
)
|
||||||
# TODO : change this field for a ForeinKey to Article
|
# TODO : change this field for a ForeinKey to Article
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Article")
|
verbose_name=_("article")
|
||||||
)
|
)
|
||||||
# TODO : change prix to price
|
# TODO : change prix to price
|
||||||
# TODO : this field is not needed if you use Article ForeignKey
|
# TODO : this field is not needed if you use Article ForeignKey
|
||||||
prix = models.DecimalField(
|
prix = models.DecimalField(
|
||||||
max_digits=5,
|
max_digits=5,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
verbose_name=_l("Price"))
|
verbose_name=_("price"))
|
||||||
# TODO : this field is not needed if you use Article ForeignKey
|
# TODO : this field is not needed if you use Article ForeignKey
|
||||||
duration = models.PositiveIntegerField(
|
duration = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name=_l("Duration (in whole month)")
|
verbose_name=_("duration (in months)")
|
||||||
)
|
)
|
||||||
# TODO : this field is not needed if you use Article ForeignKey
|
# TODO : this field is not needed if you use Article ForeignKey
|
||||||
type_cotisation = models.CharField(
|
type_cotisation = models.CharField(
|
||||||
|
@ -320,16 +332,16 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Type of cotisation")
|
verbose_name=_("subscription type")
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
('view_vente', _l("Can see a purchase's details")),
|
('view_vente', _("Can view a purchase object")),
|
||||||
('change_all_vente', _l("Can edit all the previous purchases")),
|
('change_all_vente', _("Can edit all the previous purchases")),
|
||||||
)
|
)
|
||||||
verbose_name = _l("Purchase")
|
verbose_name = _("purchase")
|
||||||
verbose_name_plural = _l("Purchases")
|
verbose_name_plural = _("purchases")
|
||||||
|
|
||||||
# TODO : change prix_total to total_price
|
# TODO : change prix_total to total_price
|
||||||
def prix_total(self):
|
def prix_total(self):
|
||||||
|
@ -355,6 +367,10 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
cotisation_type defined (which means the article sold represents
|
cotisation_type defined (which means the article sold represents
|
||||||
a cotisation)
|
a cotisation)
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
invoice = self.facture.facture
|
||||||
|
except Facture.DoesNotExist:
|
||||||
|
return
|
||||||
if not hasattr(self, 'cotisation') and self.type_cotisation:
|
if not hasattr(self, 'cotisation') and self.type_cotisation:
|
||||||
cotisation = Cotisation(vente=self)
|
cotisation = Cotisation(vente=self)
|
||||||
cotisation.type_cotisation = self.type_cotisation
|
cotisation.type_cotisation = self.type_cotisation
|
||||||
|
@ -362,7 +378,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
end_cotisation = Cotisation.objects.filter(
|
end_cotisation = Cotisation.objects.filter(
|
||||||
vente__in=Vente.objects.filter(
|
vente__in=Vente.objects.filter(
|
||||||
facture__in=Facture.objects.filter(
|
facture__in=Facture.objects.filter(
|
||||||
user=self.facture.user
|
user=invoice.user
|
||||||
).exclude(valid=False))
|
).exclude(valid=False))
|
||||||
).filter(
|
).filter(
|
||||||
Q(type_cotisation='All') |
|
Q(type_cotisation='All') |
|
||||||
|
@ -371,9 +387,9 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
date_start__lt=date_start
|
date_start__lt=date_start
|
||||||
).aggregate(Max('date_end'))['date_end__max']
|
).aggregate(Max('date_end'))['date_end__max']
|
||||||
elif self.type_cotisation == "Adhesion":
|
elif self.type_cotisation == "Adhesion":
|
||||||
end_cotisation = self.facture.user.end_adhesion()
|
end_cotisation = invoice.user.end_adhesion()
|
||||||
else:
|
else:
|
||||||
end_cotisation = self.facture.user.end_connexion()
|
end_cotisation = invoice.user.end_connexion()
|
||||||
date_start = date_start or timezone.now()
|
date_start = date_start or timezone.now()
|
||||||
end_cotisation = end_cotisation or date_start
|
end_cotisation = end_cotisation or date_start
|
||||||
date_max = max(end_cotisation, date_start)
|
date_max = max(end_cotisation, date_start)
|
||||||
|
@ -392,7 +408,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
# Checking that if a cotisation is specified, there is also a duration
|
# Checking that if a cotisation is specified, there is also a duration
|
||||||
if self.type_cotisation and not self.duration:
|
if self.type_cotisation and not self.duration:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("A cotisation should always have a duration.")
|
_("Duration must be specified for a subscription.")
|
||||||
)
|
)
|
||||||
self.update_cotisation()
|
self.update_cotisation()
|
||||||
super(Vente, self).save(*args, **kwargs)
|
super(Vente, self).save(*args, **kwargs)
|
||||||
|
@ -428,7 +444,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
def can_view(self, user_request, *_args, **_kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
if (not user_request.has_perm('cotisations.view_vente') and
|
if (not user_request.has_perm('cotisations.view_vente') and
|
||||||
self.facture.user != user_request):
|
self.facture.user != user_request):
|
||||||
return False, _("You don't have the right to see someone "
|
return False, _("You don't have the right to view someone "
|
||||||
"else's purchase history.")
|
"else's purchase history.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
@ -445,6 +461,10 @@ def vente_post_save(**kwargs):
|
||||||
LDAP user when a purchase has been saved.
|
LDAP user when a purchase has been saved.
|
||||||
"""
|
"""
|
||||||
purchase = kwargs['instance']
|
purchase = kwargs['instance']
|
||||||
|
try:
|
||||||
|
purchase.facture.facture
|
||||||
|
except Facture.DoesNotExist:
|
||||||
|
return
|
||||||
if hasattr(purchase, 'cotisation'):
|
if hasattr(purchase, 'cotisation'):
|
||||||
purchase.cotisation.vente = purchase
|
purchase.cotisation.vente = purchase
|
||||||
purchase.cotisation.save()
|
purchase.cotisation.save()
|
||||||
|
@ -462,8 +482,12 @@ def vente_post_delete(**kwargs):
|
||||||
Synchronise the LDAP user after a purchase has been deleted.
|
Synchronise the LDAP user after a purchase has been deleted.
|
||||||
"""
|
"""
|
||||||
purchase = kwargs['instance']
|
purchase = kwargs['instance']
|
||||||
|
try:
|
||||||
|
invoice = purchase.facture.facture
|
||||||
|
except Facture.DoesNotExist:
|
||||||
|
return
|
||||||
if purchase.type_cotisation:
|
if purchase.type_cotisation:
|
||||||
user = purchase.facture.user
|
user = invoice.user
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
||||||
|
|
||||||
|
|
||||||
|
@ -483,38 +507,38 @@ class Article(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
# TODO : Either use TYPE or TYPES in both choices but not both
|
# TODO : Either use TYPE or TYPES in both choices but not both
|
||||||
USER_TYPES = (
|
USER_TYPES = (
|
||||||
('Adherent', _l("Member")),
|
('Adherent', _("Member")),
|
||||||
('Club', _l("Club")),
|
('Club', _("Club")),
|
||||||
('All', _l("Both of them")),
|
('All', _("Both of them")),
|
||||||
)
|
)
|
||||||
|
|
||||||
COTISATION_TYPE = (
|
COTISATION_TYPE = (
|
||||||
('Connexion', _l("Connexion")),
|
('Connexion', _("Connection")),
|
||||||
('Adhesion', _l("Membership")),
|
('Adhesion', _("Membership")),
|
||||||
('All', _l("Both of them")),
|
('All', _("Both of them")),
|
||||||
)
|
)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Designation")
|
verbose_name=_("designation")
|
||||||
)
|
)
|
||||||
# TODO : change prix to price
|
# TODO : change prix to price
|
||||||
prix = models.DecimalField(
|
prix = models.DecimalField(
|
||||||
max_digits=5,
|
max_digits=5,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
verbose_name=_l("Unitary price")
|
verbose_name=_("unit price")
|
||||||
)
|
)
|
||||||
duration = models.PositiveIntegerField(
|
duration = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(0)],
|
validators=[MinValueValidator(0)],
|
||||||
verbose_name=_l("Duration (in whole month)")
|
verbose_name=_("duration (in months)")
|
||||||
)
|
)
|
||||||
type_user = models.CharField(
|
type_user = models.CharField(
|
||||||
choices=USER_TYPES,
|
choices=USER_TYPES,
|
||||||
default='All',
|
default='All',
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Type of users concerned")
|
verbose_name=_("type of users concerned")
|
||||||
)
|
)
|
||||||
type_cotisation = models.CharField(
|
type_cotisation = models.CharField(
|
||||||
choices=COTISATION_TYPE,
|
choices=COTISATION_TYPE,
|
||||||
|
@ -522,31 +546,31 @@ class Article(RevMixin, AclMixin, models.Model):
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Type of cotisation")
|
verbose_name=_("subscription type")
|
||||||
)
|
)
|
||||||
available_for_everyone = models.BooleanField(
|
available_for_everyone = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name=_l("Is available for every user")
|
verbose_name=_("is available for every user")
|
||||||
)
|
)
|
||||||
|
|
||||||
unique_together = ('name', 'type_user')
|
unique_together = ('name', 'type_user')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
('view_article', _l("Can see an article's details")),
|
('view_article', _("Can view an article object")),
|
||||||
('buy_every_article', _l("Can buy every_article"))
|
('buy_every_article', _("Can buy every article"))
|
||||||
)
|
)
|
||||||
verbose_name = "Article"
|
verbose_name = "article"
|
||||||
verbose_name_plural = "Articles"
|
verbose_name_plural = "articles"
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.name.lower() == 'solde':
|
if self.name.lower() == 'solde':
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Solde is a reserved article name")
|
_("Balance is a reserved article name.")
|
||||||
)
|
)
|
||||||
if self.type_cotisation and not self.duration:
|
if self.type_cotisation and not self.duration:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Duration must be specified for a cotisation")
|
_("Duration must be specified for a subscription.")
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -567,19 +591,28 @@ class Article(RevMixin, AclMixin, models.Model):
|
||||||
self.available_for_everyone
|
self.available_for_everyone
|
||||||
or user.has_perm('cotisations.buy_every_article')
|
or user.has_perm('cotisations.buy_every_article')
|
||||||
or user.has_perm('cotisations.add_facture'),
|
or user.has_perm('cotisations.add_facture'),
|
||||||
_("You cannot buy this Article.")
|
_("You can't buy this article.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_allowed_articles(cls, user):
|
def find_allowed_articles(cls, user, target_user):
|
||||||
"""Finds every allowed articles for an user.
|
"""Finds every allowed articles for an user, on a target user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user: The user requesting articles.
|
user: The user requesting articles.
|
||||||
|
target_user: The user to sell articles
|
||||||
"""
|
"""
|
||||||
|
if target_user.is_class_club:
|
||||||
|
objects_pool = cls.objects.filter(
|
||||||
|
Q(type_user='All') | Q(type_user='Club')
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
objects_pool = cls.objects.filter(
|
||||||
|
Q(type_user='All') | Q(type_user='Adherent')
|
||||||
|
)
|
||||||
if user.has_perm('cotisations.buy_every_article'):
|
if user.has_perm('cotisations.buy_every_article'):
|
||||||
return cls.objects.all()
|
return objects_pool
|
||||||
return cls.objects.filter(available_for_everyone=True)
|
return objects_pool.filter(available_for_everyone=True)
|
||||||
|
|
||||||
|
|
||||||
class Banque(RevMixin, AclMixin, models.Model):
|
class Banque(RevMixin, AclMixin, models.Model):
|
||||||
|
@ -593,15 +626,14 @@ class Banque(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Name")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
('view_banque', _l("Can see a bank's details")),
|
('view_banque', _("Can view a bank object")),
|
||||||
)
|
)
|
||||||
verbose_name = _l("Bank")
|
verbose_name = _("bank")
|
||||||
verbose_name_plural = _l("Banks")
|
verbose_name_plural = _("banks")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -619,33 +651,33 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
# TODO : change moyen to method
|
# TODO : change moyen to method
|
||||||
moyen = models.CharField(
|
moyen = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_l("Method")
|
verbose_name=_("method")
|
||||||
)
|
)
|
||||||
available_for_everyone = models.BooleanField(
|
available_for_everyone = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name=_l("Is available for every user")
|
verbose_name=_("is available for every user")
|
||||||
)
|
)
|
||||||
is_balance = models.BooleanField(
|
is_balance = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
editable=False,
|
editable=False,
|
||||||
verbose_name=_l("Is user balance"),
|
verbose_name=_("is user balance"),
|
||||||
help_text=_l("There should be only one balance payment method."),
|
help_text=_("There should be only one balance payment method."),
|
||||||
validators=[check_no_balance]
|
validators=[check_no_balance]
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
('view_paiement', _l("Can see a payement's details")),
|
('view_paiement', _("Can view a payment method object")),
|
||||||
('use_every_payment', _l("Can use every payement")),
|
('use_every_payment', _("Can use every payment method")),
|
||||||
)
|
)
|
||||||
verbose_name = _l("Payment method")
|
verbose_name = _("payment method")
|
||||||
verbose_name_plural = _l("Payment methods")
|
verbose_name_plural = _("payment methods")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.moyen
|
return self.moyen
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""l
|
||||||
Override of the herited clean function to get a correct name
|
Override of the herited clean function to get a correct name
|
||||||
"""
|
"""
|
||||||
self.moyen = self.moyen.title()
|
self.moyen = self.moyen.title()
|
||||||
|
@ -673,8 +705,8 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
if any(sell.type_cotisation for sell in invoice.vente_set.all()):
|
if any(sell.type_cotisation for sell in invoice.vente_set.all()):
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The cotisation of %(member_name)s has been \
|
_("The subscription of %(member_name)s was extended to"
|
||||||
extended to %(end_date)s.") % {
|
" %(end_date)s.") % {
|
||||||
'member_name': invoice.user.pseudo,
|
'member_name': invoice.user.pseudo,
|
||||||
'end_date': invoice.user.end_adhesion()
|
'end_date': invoice.user.end_adhesion()
|
||||||
}
|
}
|
||||||
|
@ -683,7 +715,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
else:
|
else:
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The invoice has been created.")
|
_("The invoice was created.")
|
||||||
)
|
)
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
|
@ -704,7 +736,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
self.available_for_everyone
|
self.available_for_everyone
|
||||||
or user.has_perm('cotisations.use_every_payment')
|
or user.has_perm('cotisations.use_every_payment')
|
||||||
or user.has_perm('cotisations.add_facture'),
|
or user.has_perm('cotisations.add_facture'),
|
||||||
_("You cannot use this Payment.")
|
_("You can't use this payment method.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -722,7 +754,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
p = find_payment_method(self)
|
p = find_payment_method(self)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
return p._meta.verbose_name
|
return p._meta.verbose_name
|
||||||
return _("No custom payment method")
|
return _("No custom payment method.")
|
||||||
|
|
||||||
|
|
||||||
class Cotisation(RevMixin, AclMixin, models.Model):
|
class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
|
@ -738,9 +770,9 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
COTISATION_TYPE = (
|
COTISATION_TYPE = (
|
||||||
('Connexion', _l("Connexion")),
|
('Connexion', _("Connection")),
|
||||||
('Adhesion', _l("Membership")),
|
('Adhesion', _("Membership")),
|
||||||
('All', _l("Both of them")),
|
('All', _("Both of them")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO : change vente to purchase
|
# TODO : change vente to purchase
|
||||||
|
@ -748,34 +780,36 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
'Vente',
|
'Vente',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name=_l("Purchase")
|
verbose_name=_("purchase")
|
||||||
)
|
)
|
||||||
type_cotisation = models.CharField(
|
type_cotisation = models.CharField(
|
||||||
choices=COTISATION_TYPE,
|
choices=COTISATION_TYPE,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
default='All',
|
default='All',
|
||||||
verbose_name=_l("Type of cotisation")
|
verbose_name=_("subscription type")
|
||||||
)
|
)
|
||||||
date_start = models.DateTimeField(
|
date_start = models.DateTimeField(
|
||||||
verbose_name=_l("Starting date")
|
verbose_name=_("start date")
|
||||||
)
|
)
|
||||||
date_end = models.DateTimeField(
|
date_end = models.DateTimeField(
|
||||||
verbose_name=_l("Ending date")
|
verbose_name=_("end date")
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
('view_cotisation', _l("Can see a cotisation's details")),
|
('view_cotisation', _("Can view a subscription object")),
|
||||||
('change_all_cotisation', _l("Can edit the previous cotisations")),
|
('change_all_cotisation', _("Can edit the previous subscriptions")),
|
||||||
)
|
)
|
||||||
|
verbose_name = _("subscription")
|
||||||
|
verbose_name_plural = _("subscriptions")
|
||||||
|
|
||||||
def can_edit(self, user_request, *_args, **_kwargs):
|
def can_edit(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.change_cotisation'):
|
if not user_request.has_perm('cotisations.change_cotisation'):
|
||||||
return False, _("You don't have the right to edit a cotisation.")
|
return False, _("You don't have the right to edit a subscription.")
|
||||||
elif not user_request.has_perm('cotisations.change_all_cotisation') \
|
elif not user_request.has_perm('cotisations.change_all_cotisation') \
|
||||||
and (self.vente.facture.control or
|
and (self.vente.facture.control or
|
||||||
not self.vente.facture.valid):
|
not self.vente.facture.valid):
|
||||||
return False, _("You don't have the right to edit a cotisation "
|
return False, _("You don't have the right to edit a subscription "
|
||||||
"already controlled or invalidated.")
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
@ -783,9 +817,9 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
def can_delete(self, user_request, *_args, **_kwargs):
|
def can_delete(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.delete_cotisation'):
|
if not user_request.has_perm('cotisations.delete_cotisation'):
|
||||||
return False, _("You don't have the right to delete a "
|
return False, _("You don't have the right to delete a "
|
||||||
"cotisation.")
|
"subscription.")
|
||||||
if self.vente.facture.control or not self.vente.facture.valid:
|
if self.vente.facture.control or not self.vente.facture.valid:
|
||||||
return False, _("You don't have the right to delete a cotisation "
|
return False, _("You don't have the right to delete a subscription "
|
||||||
"already controlled or invalidated.")
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
@ -793,8 +827,8 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
def can_view(self, user_request, *_args, **_kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.view_cotisation') and\
|
if not user_request.has_perm('cotisations.view_cotisation') and\
|
||||||
self.vente.facture.user != user_request:
|
self.vente.facture.user != user_request:
|
||||||
return False, _("You don't have the right to see someone else's "
|
return False, _("You don't have the right to view someone else's "
|
||||||
"cotisation history.")
|
"subscription history.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -822,3 +856,4 @@ def cotisation_post_delete(**_kwargs):
|
||||||
"""
|
"""
|
||||||
regen('mac_ip_list')
|
regen('mac_ip_list')
|
||||||
regen('mailing')
|
regen('mailing')
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ class BalancePayment(PaymentMethodMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _l("User Balance")
|
verbose_name = _("user balance")
|
||||||
|
|
||||||
payment = models.OneToOneField(
|
payment = models.OneToOneField(
|
||||||
Paiement,
|
Paiement,
|
||||||
|
@ -45,8 +44,8 @@ class BalancePayment(PaymentMethodMixin, models.Model):
|
||||||
editable=False
|
editable=False
|
||||||
)
|
)
|
||||||
minimum_balance = models.DecimalField(
|
minimum_balance = models.DecimalField(
|
||||||
verbose_name=_l("Minimum balance"),
|
verbose_name=_("Minimum balance"),
|
||||||
help_text=_l("The minimal amount of money allowed for the balance"
|
help_text=_("The minimal amount of money allowed for the balance"
|
||||||
" at the end of a payment. You can specify negative "
|
" at the end of a payment. You can specify negative "
|
||||||
"amount."
|
"amount."
|
||||||
),
|
),
|
||||||
|
@ -55,8 +54,8 @@ class BalancePayment(PaymentMethodMixin, models.Model):
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
maximum_balance = models.DecimalField(
|
maximum_balance = models.DecimalField(
|
||||||
verbose_name=_l("Maximum balance"),
|
verbose_name=_("Maximum balance"),
|
||||||
help_text=_l("The maximal amount of money allowed for the balance."),
|
help_text=_("The maximal amount of money allowed for the balance."),
|
||||||
max_digits=5,
|
max_digits=5,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
default=50,
|
default=50,
|
||||||
|
@ -64,7 +63,7 @@ class BalancePayment(PaymentMethodMixin, models.Model):
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
credit_balance_allowed = models.BooleanField(
|
credit_balance_allowed = models.BooleanField(
|
||||||
verbose_name=_l("Allow user to credit their balance"),
|
verbose_name=_("Allow user to credit their balance"),
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ class BalancePayment(PaymentMethodMixin, models.Model):
|
||||||
if len(p) > 0:
|
if len(p) > 0:
|
||||||
form.add_error(
|
form.add_error(
|
||||||
'payment_method',
|
'payment_method',
|
||||||
_("There is already a payment type for user balance")
|
_("There is already a payment method for user balance.")
|
||||||
)
|
)
|
||||||
|
|
||||||
def alter_payment(self, payment):
|
def alter_payment(self, payment):
|
||||||
|
@ -118,3 +117,4 @@ class BalancePayment(PaymentMethodMixin, models.Model):
|
||||||
len(Paiement.find_allowed_payments(user_request)
|
len(Paiement.find_allowed_payments(user_request)
|
||||||
.exclude(is_balance=True)) > 0
|
.exclude(is_balance=True)) > 0
|
||||||
) and self.credit_balance_allowed
|
) and self.credit_balance_allowed
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from cotisations.models import Paiement
|
from cotisations.models import Paiement
|
||||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||||
|
@ -33,7 +33,7 @@ class ChequePayment(PaymentMethodMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _l("Cheque")
|
verbose_name = _("Cheque")
|
||||||
|
|
||||||
payment = models.OneToOneField(
|
payment = models.OneToOneField(
|
||||||
Paiement,
|
Paiement,
|
||||||
|
@ -52,3 +52,4 @@ class ChequePayment(PaymentMethodMixin, models.Model):
|
||||||
'cotisations:cheque:validate',
|
'cotisations:cheque:validate',
|
||||||
kwargs={'invoice_pk': invoice.pk}
|
kwargs={'invoice_pk': invoice.pk}
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ def cheque(request, invoice_pk):
|
||||||
if invoice.valid or not isinstance(payment_method, ChequePayment):
|
if invoice.valid or not isinstance(payment_method, ChequePayment):
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
_("You cannot pay this invoice with a cheque.")
|
_("You can't pay this invoice with a cheque.")
|
||||||
)
|
)
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
|
@ -67,3 +67,4 @@ def cheque(request, invoice_pk):
|
||||||
'amount': invoice.prix_total()
|
'amount': invoice.prix_total()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
|
||||||
|
|
||||||
from cotisations.models import Paiement
|
from cotisations.models import Paiement
|
||||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||||
|
@ -37,7 +36,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "ComNpay"
|
verbose_name = _("ComNpay")
|
||||||
|
|
||||||
payment = models.OneToOneField(
|
payment = models.OneToOneField(
|
||||||
Paiement,
|
Paiement,
|
||||||
|
@ -49,22 +48,32 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
|
||||||
max_length=255,
|
max_length=255,
|
||||||
default='',
|
default='',
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_l("ComNpay VAD Number"),
|
verbose_name=_("ComNpay VAT Number"),
|
||||||
)
|
)
|
||||||
payment_pass = AESEncryptedField(
|
payment_pass = AESEncryptedField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_l("ComNpay Secret Key"),
|
verbose_name=_("ComNpay secret key"),
|
||||||
)
|
)
|
||||||
minimum_payment = models.DecimalField(
|
minimum_payment = models.DecimalField(
|
||||||
verbose_name=_l("Minimum payment"),
|
verbose_name=_("Minimum payment"),
|
||||||
help_text=_l("The minimal amount of money you have to use when paying"
|
help_text=_("The minimal amount of money you have to use when paying"
|
||||||
" with ComNpay"),
|
" with ComNpay"),
|
||||||
max_digits=5,
|
max_digits=5,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
default=1,
|
default=1,
|
||||||
)
|
)
|
||||||
|
production = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name=_("Production mode enabled (production URL, instead of homologation)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def return_url_comnpay(self):
|
||||||
|
if self.production:
|
||||||
|
return 'https://secure.comnpay.com'
|
||||||
|
else:
|
||||||
|
return 'https://secure.homologation.comnpay.com'
|
||||||
|
|
||||||
def end_payment(self, invoice, request):
|
def end_payment(self, invoice, request):
|
||||||
"""
|
"""
|
||||||
|
@ -87,11 +96,12 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
|
||||||
"",
|
"",
|
||||||
"D"
|
"D"
|
||||||
)
|
)
|
||||||
|
|
||||||
r = {
|
r = {
|
||||||
'action': 'https://secure.homologation.comnpay.com',
|
'action': self.return_url_comnpay(),
|
||||||
'method': 'POST',
|
'method': 'POST',
|
||||||
'content': p.buildSecretHTML(
|
'content': p.buildSecretHTML(
|
||||||
_("Pay invoice no : ")+str(invoice.id),
|
_("Pay invoice number ")+str(invoice.id),
|
||||||
invoice.prix_total(),
|
invoice.prix_total(),
|
||||||
idTransaction=str(invoice.id)
|
idTransaction=str(invoice.id)
|
||||||
),
|
),
|
||||||
|
@ -103,6 +113,6 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
|
||||||
"""Checks that the price meets the requirement to be paid with ComNpay.
|
"""Checks that the price meets the requirement to be paid with ComNpay.
|
||||||
"""
|
"""
|
||||||
return ((price >= self.minimum_payment),
|
return ((price >= self.minimum_payment),
|
||||||
_('In order to pay your invoice with ComNpay'
|
_("In order to pay your invoice with ComNpay, the price must"
|
||||||
', the price must be grater than {} €')
|
" be greater than {} €.").format(self.minimum_payment))
|
||||||
.format(self.minimum_payment))
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ def accept_payment(request, factureid):
|
||||||
if invoice.valid:
|
if invoice.valid:
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The payment of %(amount)s € has been accepted.") % {
|
_("The payment of %(amount)s € was accepted.") % {
|
||||||
'amount': invoice.prix_total()
|
'amount': invoice.prix_total()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -60,8 +60,8 @@ def accept_payment(request, factureid):
|
||||||
for purchase in invoice.vente_set.all()):
|
for purchase in invoice.vente_set.all()):
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The cotisation of %(member_name)s has been \
|
_("The subscription of %(member_name)s was extended to"
|
||||||
extended to %(end_date)s.") % {
|
" %(end_date)s.") % {
|
||||||
'member_name': request.user.pseudo,
|
'member_name': request.user.pseudo,
|
||||||
'end_date': request.user.end_adhesion()
|
'end_date': request.user.end_adhesion()
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ def refuse_payment(request):
|
||||||
"""
|
"""
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
_("The payment has been refused.")
|
_("The payment was refused.")
|
||||||
)
|
)
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
|
@ -136,3 +136,4 @@ def ipn(request):
|
||||||
# Everything worked we send a reponse to Comnpay indicating that
|
# Everything worked we send a reponse to Comnpay indicating that
|
||||||
# it's ok for them to proceed
|
# it's ok for them to proceed
|
||||||
return HttpResponse("HTTP/1.0 200 OK")
|
return HttpResponse("HTTP/1.0 200 OK")
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
|
||||||
|
|
||||||
from . import PAYMENT_METHODS
|
from . import PAYMENT_METHODS
|
||||||
from cotisations.utils import find_payment_method
|
from cotisations.utils import find_payment_method
|
||||||
|
@ -58,8 +57,8 @@ class PaymentMethodForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
payment_method = forms.ChoiceField(
|
payment_method = forms.ChoiceField(
|
||||||
label=_l("Special payment method"),
|
label=_("Special payment method"),
|
||||||
help_text=_l("Warning : You will not be able to change the payment "
|
help_text=_("Warning: you will not be able to change the payment "
|
||||||
"method later. But you will be allowed to edit the other "
|
"method later. But you will be allowed to edit the other "
|
||||||
"options."
|
"options."
|
||||||
),
|
),
|
||||||
|
@ -70,7 +69,7 @@ class PaymentMethodForm(forms.Form):
|
||||||
super(PaymentMethodForm, self).__init__(*args, **kwargs)
|
super(PaymentMethodForm, self).__init__(*args, **kwargs)
|
||||||
prefix = kwargs.get('prefix', None)
|
prefix = kwargs.get('prefix', None)
|
||||||
self.fields['payment_method'].choices = [(i,p.NAME) for (i,p) in enumerate(PAYMENT_METHODS)]
|
self.fields['payment_method'].choices = [(i,p.NAME) for (i,p) in enumerate(PAYMENT_METHODS)]
|
||||||
self.fields['payment_method'].choices.insert(0, ('', _l('no')))
|
self.fields['payment_method'].choices.insert(0, ('', _('no')))
|
||||||
self.fields['payment_method'].widget.attrs = {
|
self.fields['payment_method'].widget.attrs = {
|
||||||
'id': 'paymentMethodSelect'
|
'id': 'paymentMethodSelect'
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Article" %}</th>
|
<th>{% trans "Article" %}</th>
|
||||||
<th>{% trans "Price" %}</th>
|
<th>{% trans "Price" %}</th>
|
||||||
<th>{% trans "Cotisation type" %}</th>
|
<th>{% trans "Subscription type" %}</th>
|
||||||
<th>{% trans "Duration (month)" %}</th>
|
<th>{% trans "Duration (in months)" %}</th>
|
||||||
<th>{% trans "Concerned users" %}</th>
|
<th>{% trans "Concerned users" %}</th>
|
||||||
<th>{% trans "Available for everyone" | tick %}</th>
|
<th>{% trans "Available for everyone" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -46,7 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ article.type_cotisation }}</td>
|
<td>{{ article.type_cotisation }}</td>
|
||||||
<td>{{ article.duration }}</td>
|
<td>{{ article.duration }}</td>
|
||||||
<td>{{ article.type_user }}</td>
|
<td>{{ article.type_user }}</td>
|
||||||
<td>{{ article.available_for_everyone }}</td>
|
<td>{{ article.available_for_everyone | tick }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% can_edit article %}
|
{% can_edit article %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" title="{% trans "Edit" %}" href="{% url 'cotisations:edit-article' article.id %}">
|
<a class="btn btn-primary btn-sm" role="button" title="{% trans "Edit" %}" href="{% url 'cotisations:edit-article' article.id %}">
|
||||||
|
|
|
@ -38,9 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ banque.name }}</td>
|
<td>{{ banque.name }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% can_edit banque %}
|
{% can_edit banque %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" title="{% trans "Edit" %}" href="{% url 'cotisations:edit-banque' banque.id %}">
|
{% include 'buttons/edit.html' with href='cotisations:edit-banque' id=banque.id %}
|
||||||
<i class="fa fa-edit"></i>
|
|
||||||
</a>
|
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% history_button banque %}
|
{% history_button banque %}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -49,7 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% include 'buttons/sort.html' with prefix='cotis' col='date' text=tr_date %}
|
{% include 'buttons/sort.html' with prefix='cotis' col='date' text=tr_date %}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{% trans "Invoice id" as tr_invoice_id %}
|
{% trans "Invoice ID" as tr_invoice_id %}
|
||||||
{% include 'buttons/sort.html' with prefix='cotis' col='id' text=tr_invoice_id %}
|
{% include 'buttons/sort.html' with prefix='cotis' col='id' text=tr_invoice_id %}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
@ -65,32 +65,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ facture.date }}</td>
|
<td>{{ facture.date }}</td>
|
||||||
<td>{{ facture.id }}</td>
|
<td>{{ facture.id }}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-default dropdown-toggle" type="button" id="editinvoice" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
||||||
{% trans "Edit" %}<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="editinvoice">
|
|
||||||
{% can_edit facture %}
|
{% can_edit facture %}
|
||||||
<li>
|
{% include 'buttons/edit.html' with href='cotisations:edit-facture' id=facture.id %}
|
||||||
<a href="{% url 'cotisations:edit-facture' facture.id %}">
|
|
||||||
<i class="fa fa-dollar-sign"></i> {% trans "Edit" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% acl_else %}
|
{% acl_else %}
|
||||||
<li>{% trans "Controlled invoice" %}</li>
|
{% trans "Controlled invoice" %}
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% can_delete facture %}
|
{% can_delete facture %}
|
||||||
<li>
|
{% include 'buttons/suppr.html' with href='cotisations:del-facture' id=facture.id %}
|
||||||
<a href="{% url 'cotisations:del-facture' facture.id %}">
|
|
||||||
<i class="fa fa-trash"></i> {% trans "Delete" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<li>
|
{% history_button facture %}
|
||||||
{% history_button facture text=True html_class=False%}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if facture.valid %}
|
{% if facture.valid %}
|
||||||
|
@ -109,3 +92,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% include 'pagination.html' with list=facture_list %}
|
{% include 'pagination.html' with list=facture_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
89
cotisations/templates/cotisations/aff_custom_invoice.html
Normal file
89
cotisations/templates/cotisations/aff_custom_invoice.html
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2018 Hugo Levy-Falk
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load acl %}
|
||||||
|
{% load logs_extra %}
|
||||||
|
{% load design %}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
{% if custom_invoice_list.paginator %}
|
||||||
|
{% include 'pagination.html' with list=custom_invoice_list %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{% trans "Recipient" as tr_recip %}
|
||||||
|
{% include 'buttons/sort.html' with prefix='invoice' col='user' text=tr_user %}
|
||||||
|
</th>
|
||||||
|
<th>{% trans "Designation" %}</th>
|
||||||
|
<th>{% trans "Total price" %}</th>
|
||||||
|
<th>
|
||||||
|
{% trans "Payment method" as tr_payment_method %}
|
||||||
|
{% include 'buttons/sort.html' with prefix='invoice' col='payement' text=tr_payment_method %}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{% trans "Date" as tr_date %}
|
||||||
|
{% include 'buttons/sort.html' with prefix='invoice' col='date' text=tr_date %}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{% trans "Invoice ID" as tr_invoice_id %}
|
||||||
|
{% include 'buttons/sort.html' with prefix='invoice' col='id' text=tr_invoice_id %}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{% trans "Paid" as tr_invoice_paid%}
|
||||||
|
{% include 'buttons/sort.html' with prefix='invoice' col='paid' text=tr_invoice_paid %}
|
||||||
|
</th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for invoice in custom_invoice_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ invoice.recipient }}</td>
|
||||||
|
<td>{{ invoice.name }}</td>
|
||||||
|
<td>{{ invoice.prix_total }}</td>
|
||||||
|
<td>{{ invoice.payment }}</td>
|
||||||
|
<td>{{ invoice.date }}</td>
|
||||||
|
<td>{{ invoice.id }}</td>
|
||||||
|
<td>{{ invoice.paid|tick }}</td>
|
||||||
|
<td>
|
||||||
|
{% can_edit invoice %}
|
||||||
|
{% include 'buttons/edit.html' with href='cotisations:edit-custom-invoice' id=invoice.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
{% can_delete invoice %}
|
||||||
|
{% include 'buttons/suppr.html' with href='cotisations:del-custom-invoice' id=invoice.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
{% history_button invoice %}
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:custom-invoice-pdf' invoice.id %}">
|
||||||
|
<i class="fa fa-file-pdf"></i> {% trans "PDF" %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if custom_invoice_list.paginator %}
|
||||||
|
{% include 'pagination.html' with list=custom_invoice_list %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
|
@ -30,17 +30,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% block title %}{% trans "Invoice control" %}{% endblock %}
|
{% block title %}{% trans "Invoice control" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<h2>{% trans "Invoice control and validation" %}</h2>
|
<h2>{% trans "Invoice control and validation" %}</h2>
|
||||||
|
|
||||||
{% if facture_list.paginator %}
|
{% if facture_list.paginator %}
|
||||||
{% include 'pagination.html' with list=facture_list %}
|
{% include 'pagination.html' with list=facture_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ controlform.management_form }}
|
{{ controlform.management_form }}
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Profil" %}</th>
|
<th>{% trans "Profile" %}</th>
|
||||||
<th>
|
<th>
|
||||||
{% trans "Last name" as tr_last_name %}
|
{% trans "Last name" as tr_last_name %}
|
||||||
{% include 'buttons/sort.html' with prefix='control' col='name' text=tr_last_name %}
|
{% include 'buttons/sort.html' with prefix='control' col='name' text=tr_last_name %}
|
||||||
|
@ -50,11 +53,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_first_name %}
|
{% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_first_name %}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{% trans "Invoice id" as tr_invoice_id %}
|
{% trans "Invoice ID" as tr_invoice_id %}
|
||||||
{% include 'buttons/sort.html' with prefix='control' col='id' text=tr_invoice_id %}
|
{% include 'buttons/sort.html' with prefix='control' col='id' text=tr_invoice_id %}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{% trans "User id" as tr_user_id %}
|
{% trans "User ID" as tr_user_id %}
|
||||||
{% include 'buttons/sort.html' with prefix='control' col='user-id' text=tr_user_id %}
|
{% include 'buttons/sort.html' with prefix='control' col='user-id' text=tr_user_id %}
|
||||||
</th>
|
</th>
|
||||||
<th>{% trans "Designation" %}</th>
|
<th>{% trans "Designation" %}</th>
|
||||||
|
@ -65,7 +68,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{% trans "Date" as tr_date %}
|
{% trans "Date" as tr_date %}
|
||||||
{% include 'buttons/sort.html' with prefix='control' col='date' text=tr_date %}i
|
{% include 'buttons/sort.html' with prefix='control' col='date' text=tr_date %}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{% trans "Validated" as tr_validated %}
|
{% trans "Validated" as tr_validated %}
|
||||||
|
@ -109,3 +112,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% if facture_list.paginator %}
|
{% if facture_list.paginator %}
|
||||||
{% include 'pagination.html' with list=facture_list %}
|
{% include 'pagination.html' with list=facture_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -26,18 +26,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% trans "Deletion of cotisations" %}{% endblock %}
|
{% block title %}{% trans "Deletion of subscriptions" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h4>
|
<h4>
|
||||||
{% blocktrans %}
|
{% blocktrans %}Warning: are you sure you really want to delete this {{ object_name }} object ( {{ objet }} )?{% endblocktrans %}
|
||||||
Warning. Are you sure you really want te delete this {{ object_name }} object ( {{ objet }} ) ?
|
|
||||||
{% endblocktrans %}
|
|
||||||
</h4>
|
</h4>
|
||||||
{% trans "Confirm" as tr_confirm %}
|
{% trans "Confirm" as tr_confirm %}
|
||||||
{% bootstrap_button tr_confirm button_type='submit' icon='trash' %}
|
{% bootstrap_button tr_confirm button_type='submit' icon='trash' %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load massive_bootstrap_form %}
|
{% load massive_bootstrap_form %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% trans "Invoices creation and edition" %}{% endblock %}
|
{% block title %}{% trans "Creation and editing of invoices" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% bootstrap_form_errors factureform %}
|
{% bootstrap_form_errors factureform %}
|
||||||
|
@ -62,3 +62,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
22
cotisations/templates/cotisations/email_invoice
Normal file
22
cotisations/templates/cotisations/email_invoice
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
=== English version below ===
|
||||||
|
|
||||||
|
Bonjour {{name}},
|
||||||
|
|
||||||
|
Nous vous remercions pour votre achat auprès de {{asso_name}} et nous vous en joignons la facture.
|
||||||
|
|
||||||
|
En cas de question, n’hésitez pas à nous contacter par mail à {{contact_mail}}.
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L’équipe de {{asso_name}}
|
||||||
|
|
||||||
|
|
||||||
|
=== English version ===
|
||||||
|
|
||||||
|
Dear {{name}},
|
||||||
|
|
||||||
|
Thank you for your purchase. Here is your invoice.
|
||||||
|
|
||||||
|
Should you need extra information, you can email us at {{contact_mail}}.
|
||||||
|
|
||||||
|
Best regards,
|
||||||
|
{{ asso_name }}'s team
|
|
@ -27,20 +27,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load staticfiles%}
|
{% load staticfiles%}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% trans "Invoices creation and edition" %}{% endblock %}
|
{% block title %}{% trans "Creation and editing of invoices" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if title %}
|
{% if title %}
|
||||||
<h3>{{ title }}</h3>
|
<h3>{{ title }}</h3>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>{% trans "New invoice" %}</h3>
|
<h3>{% trans "New invoice" %}</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if max_balance %}
|
{% if max_balance %}
|
||||||
<h4>{% trans "Maximum allowed balance : "%}{{max_balance}} €</h4>
|
<h4>{% blocktrans %}Maximum allowed balance: {{ max_balance }} €{% endblocktrans %}</h4>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if balance is not None %}
|
{% if balance is not None %}
|
||||||
<p>
|
<p>
|
||||||
{% trans "Current balance :" %} {{ balance }} €
|
{% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -68,9 +69,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</div>
|
</div>
|
||||||
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add an article"%}" id="add_one">
|
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add an article"%}" id="add_one">
|
||||||
<p>
|
<p>
|
||||||
{% blocktrans %}
|
{% blocktrans %}Total price: <span id="total_price">0,00</span> €{% endblocktrans %}
|
||||||
Total price : <span id="total_price">0,00</span> €
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% bootstrap_button action_name button_type='submit' icon='star' %}
|
{% bootstrap_button action_name button_type='submit' icon='star' %}
|
||||||
|
@ -183,3 +182,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% block title %}{% trans "Invoices" %}{% endblock %}
|
{% block title %}{% trans "Invoices" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>{% trans "Cotisations" %}</h2>
|
<h2>{% trans "Subscriptions" %}</h2>
|
||||||
{% include 'cotisations/aff_cotisations.html' with facture_list=facture_list %}
|
{% include 'cotisations/aff_cotisations.html' with facture_list=facture_list %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}">
|
||||||
<i class="fa fa-trash"></i> {% trans "Delete article types" %}
|
<i class="fa fa-trash"></i> {% trans "Delete one or several article types" %}
|
||||||
</a>
|
</a>
|
||||||
{% include 'cotisations/aff_article.html' with article_list=article_list %}
|
{% include 'cotisations/aff_article.html' with article_list=article_list %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}">
|
||||||
<i class="fa fa-trash"></i> {% trans "Delete banks" %}
|
<i class="fa fa-trash"></i> {% trans "Delete one or several banks" %}
|
||||||
</a>
|
</a>
|
||||||
{% include 'cotisations/aff_banque.html' with banque_list=banque_list %}
|
{% include 'cotisations/aff_banque.html' with banque_list=banque_list %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
36
cotisations/templates/cotisations/index_custom_invoice.html
Normal file
36
cotisations/templates/cotisations/index_custom_invoice.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends "cotisations/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Custom invoices" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>{% trans "Custom invoices list" %}</h2>
|
||||||
|
{% can_create CustomInvoice %}
|
||||||
|
{% include "buttons/add.html" with href='cotisations:new-custom-invoice'%}
|
||||||
|
{% acl_end %}
|
||||||
|
{% include 'cotisations/aff_custom_invoice.html' with custom_invoice_list=custom_invoice_list %}
|
||||||
|
{% endblock %}
|
|
@ -27,17 +27,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% trans "Payments" %}{% endblock %}
|
{% block title %}{% trans "Payment methods" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>{% trans "Payment types list" %}</h2>
|
<h2>{% trans "List of payment methods" %}</h2>
|
||||||
{% can_create Paiement %}
|
{% can_create Paiement %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-paiement' %}">
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-paiement' %}">
|
||||||
<i class="fa fa-cart-plus"></i> {% trans "Add a payment type" %}
|
<i class="fa fa-cart-plus"></i> {% trans "Add a payment method" %}
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-paiement' %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-paiement' %}">
|
||||||
<i class="fa fa-trash"></i> {% trans "Delete payment types" %}
|
<i class="fa fa-trash"></i> {% trans "Delete one or several payment methods" %}
|
||||||
</a>
|
</a>
|
||||||
{% include 'cotisations/aff_paiement.html' with paiement_list=paiement_list %}
|
{% include 'cotisations/aff_paiement.html' with paiement_list=paiement_list %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -31,9 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>
|
<h3>
|
||||||
{% blocktrans %}
|
{% blocktrans %}Pay {{ amount }} €{% endblocktrans %}
|
||||||
Pay {{ amount }} €
|
|
||||||
{% endblocktrans %}
|
|
||||||
</h3>
|
</h3>
|
||||||
<form class="form" method="{{ method|default:"post" }}" action="{{ action }}">
|
<form class="form" method="{{ method|default:"post" }}" action="{{ action }}">
|
||||||
{{ content | safe }}
|
{{ content | safe }}
|
||||||
|
@ -45,3 +43,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% bootstrap_button tr_pay button_type='submit' icon='piggy-bank' %}
|
{% bootstrap_button tr_pay button_type='submit' icon='piggy-bank' %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block sidebar %}
|
{% block sidebar %}
|
||||||
{% can_change Facture pdf %}
|
{% can_create CustomInvoice %}
|
||||||
<a class="list-group-item list-group-item-success" href="{% url "cotisations:new-facture-pdf" %}">
|
<a class="list-group-item list-group-item-success" href="{% url "cotisations:new-custom-invoice" %}">
|
||||||
<i class="fa fa-plus"></i> {% trans "Create an invoice" %}
|
<i class="fa fa-plus"></i> {% trans "Create an invoice" %}
|
||||||
</a>
|
</a>
|
||||||
<a class="list-group-item list-group-item-warning" href="{% url "cotisations:control" %}">
|
<a class="list-group-item list-group-item-warning" href="{% url "cotisations:control" %}">
|
||||||
|
@ -40,6 +40,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<i class="fa fa-list-ul"></i> {% trans "Invoices" %}
|
<i class="fa fa-list-ul"></i> {% trans "Invoices" %}
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
{% can_view_all CustomInvoice %}
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-custom-invoice" %}">
|
||||||
|
<i class="fa fa-list-ul"></i> {% trans "Custom invoices" %}
|
||||||
|
</a>
|
||||||
|
{% acl_end %}
|
||||||
{% can_view_all Article %}
|
{% can_view_all Article %}
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-article" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "cotisations:index-article" %}">
|
||||||
<i class="fa fa-list-ul"></i> {% trans "Available articles" %}
|
<i class="fa fa-list-ul"></i> {% trans "Available articles" %}
|
||||||
|
@ -56,3 +61,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# coding: utf-8
|
||||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
|
@ -24,6 +25,7 @@ Module in charge of rendering some LaTex templates.
|
||||||
Used to generated PDF invoice.
|
Used to generated PDF invoice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import tempfile
|
import tempfile
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
import os
|
import os
|
||||||
|
@ -61,18 +63,24 @@ def render_invoice(_request, ctx={}):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def render_tex(_request, template, ctx={}):
|
def create_pdf(template, ctx={}):
|
||||||
"""
|
"""Creates and returns a PDF from a LaTeX template using pdflatex.
|
||||||
Creates a PDF from a LaTex templates using pdflatex.
|
|
||||||
Writes it in a temporary directory and send back an HTTP response for
|
It create a temporary file for the PDF then read it to return its content.
|
||||||
accessing this file.
|
|
||||||
|
Args:
|
||||||
|
template: Path to the LaTeX template.
|
||||||
|
ctx: Dict with the context for rendering the template.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The content of the temporary PDF file generated.
|
||||||
"""
|
"""
|
||||||
context = Context(ctx)
|
context = Context(ctx)
|
||||||
template = get_template(template)
|
template = get_template(template)
|
||||||
rendered_tpl = template.render(context).encode('utf-8')
|
rendered_tpl = template.render(context).encode('utf-8')
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tempdir:
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
for i in range(2):
|
for _ in range(2):
|
||||||
process = Popen(
|
process = Popen(
|
||||||
['pdflatex', '-output-directory', tempdir],
|
['pdflatex', '-output-directory', tempdir],
|
||||||
stdin=PIPE,
|
stdin=PIPE,
|
||||||
|
@ -81,6 +89,25 @@ def render_tex(_request, template, ctx={}):
|
||||||
process.communicate(rendered_tpl)
|
process.communicate(rendered_tpl)
|
||||||
with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f:
|
with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f:
|
||||||
pdf = f.read()
|
pdf = f.read()
|
||||||
|
|
||||||
|
return pdf
|
||||||
|
|
||||||
|
|
||||||
|
def render_tex(_request, template, ctx={}):
|
||||||
|
"""Creates a PDF from a LaTex templates using pdflatex.
|
||||||
|
|
||||||
|
Calls `create_pdf` and send back an HTTP response for
|
||||||
|
accessing this file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
_request: Unused, but allow using this function as a Django view.
|
||||||
|
template: Path to the LaTeX template.
|
||||||
|
ctx: Dict with the context for rendering the template.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An HttpResponse with type `application/pdf` containing the PDF file.
|
||||||
|
"""
|
||||||
|
pdf = create_pdf(template, ctx)
|
||||||
r = HttpResponse(content_type='application/pdf')
|
r = HttpResponse(content_type='application/pdf')
|
||||||
r.write(pdf)
|
r.write(pdf)
|
||||||
return r
|
return r
|
||||||
|
|
|
@ -52,9 +52,29 @@ urlpatterns = [
|
||||||
name='facture-pdf'
|
name='facture-pdf'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^new_facture_pdf/$',
|
r'^index_custom_invoice/$',
|
||||||
views.new_facture_pdf,
|
views.index_custom_invoice,
|
||||||
name='new-facture-pdf'
|
name='index-custom-invoice'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^new_custom_invoice/$',
|
||||||
|
views.new_custom_invoice,
|
||||||
|
name='new-custom-invoice'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^edit_custom_invoice/(?P<custominvoiceid>[0-9]+)$',
|
||||||
|
views.edit_custom_invoice,
|
||||||
|
name='edit-custom-invoice'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^custom_invoice_pdf/(?P<custominvoiceid>[0-9]+)$',
|
||||||
|
views.custom_invoice_pdf,
|
||||||
|
name='custom-invoice-pdf',
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^del_custom_invoice/(?P<custominvoiceid>[0-9]+)$',
|
||||||
|
views.del_custom_invoice,
|
||||||
|
name='del-custom-invoice'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^credit_solde/(?P<userid>[0-9]+)$',
|
r'^credit_solde/(?P<userid>[0-9]+)$',
|
||||||
|
|
|
@ -19,6 +19,16 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.template.loader import get_template
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
|
|
||||||
|
from .tex import create_pdf
|
||||||
|
from preferences.models import AssoOption, GeneralOption
|
||||||
|
from re2o.settings import LOGO_PATH
|
||||||
|
from re2o import settings
|
||||||
|
|
||||||
|
|
||||||
def find_payment_method(payment):
|
def find_payment_method(payment):
|
||||||
"""Finds the payment method associated to the payment if it exists."""
|
"""Finds the payment method associated to the payment if it exists."""
|
||||||
|
@ -30,3 +40,56 @@ def find_payment_method(payment):
|
||||||
except method.PaymentMethod.DoesNotExist:
|
except method.PaymentMethod.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def send_mail_invoice(invoice):
|
||||||
|
"""Creates the pdf of the invoice and sends it by email to the client"""
|
||||||
|
purchases_info = []
|
||||||
|
for purchase in invoice.vente_set.all():
|
||||||
|
purchases_info.append({
|
||||||
|
'name': purchase.name,
|
||||||
|
'price': purchase.prix,
|
||||||
|
'quantity': purchase.number,
|
||||||
|
'total_price': purchase.prix_total
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
'paid': True,
|
||||||
|
'fid': invoice.id,
|
||||||
|
'DATE': invoice.date,
|
||||||
|
'recipient_name': "{} {}".format(
|
||||||
|
invoice.user.name,
|
||||||
|
invoice.user.surname
|
||||||
|
),
|
||||||
|
'address': invoice.user.room,
|
||||||
|
'article': purchases_info,
|
||||||
|
'total': invoice.prix_total(),
|
||||||
|
'asso_name': AssoOption.get_cached_value('name'),
|
||||||
|
'line1': AssoOption.get_cached_value('adresse1'),
|
||||||
|
'line2': AssoOption.get_cached_value('adresse2'),
|
||||||
|
'siret': AssoOption.get_cached_value('siret'),
|
||||||
|
'email': AssoOption.get_cached_value('contact'),
|
||||||
|
'phone': AssoOption.get_cached_value('telephone'),
|
||||||
|
'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf = create_pdf('cotisations/factures.tex', ctx)
|
||||||
|
template = get_template('cotisations/email_invoice')
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
'name': "{} {}".format(
|
||||||
|
invoice.user.name,
|
||||||
|
invoice.user.surname
|
||||||
|
),
|
||||||
|
'contact_mail': AssoOption.get_cached_value('contact'),
|
||||||
|
'asso_name': AssoOption.get_cached_value('name')
|
||||||
|
}
|
||||||
|
|
||||||
|
mail = EmailMessage(
|
||||||
|
'Votre facture / Your invoice',
|
||||||
|
template.render(ctx),
|
||||||
|
GeneralOption.get_cached_value('email_from'),
|
||||||
|
[invoice.user.email],
|
||||||
|
attachments=[('invoice.pdf', pdf, 'application/pdf')]
|
||||||
|
)
|
||||||
|
mail.send()
|
||||||
|
|
|
@ -17,5 +17,6 @@ def check_no_balance(is_balance):
|
||||||
p = Paiement.objects.filter(is_balance=True)
|
p = Paiement.objects.filter(is_balance=True)
|
||||||
if len(p) > 0:
|
if len(p) > 0:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("There are already payment method(s) for user balance")
|
_("There is already a payment method for user balance.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,15 @@ from re2o.acl import (
|
||||||
can_change,
|
can_change,
|
||||||
)
|
)
|
||||||
from preferences.models import AssoOption, GeneralOption
|
from preferences.models import AssoOption, GeneralOption
|
||||||
from .models import Facture, Article, Vente, Paiement, Banque
|
from .models import (
|
||||||
|
Facture,
|
||||||
|
Article,
|
||||||
|
Vente,
|
||||||
|
Paiement,
|
||||||
|
Banque,
|
||||||
|
CustomInvoice,
|
||||||
|
BaseInvoice
|
||||||
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
FactureForm,
|
FactureForm,
|
||||||
ArticleForm,
|
ArticleForm,
|
||||||
|
@ -67,14 +75,13 @@ from .forms import (
|
||||||
DelPaiementForm,
|
DelPaiementForm,
|
||||||
BanqueForm,
|
BanqueForm,
|
||||||
DelBanqueForm,
|
DelBanqueForm,
|
||||||
NewFactureFormPdf,
|
SelectArticleForm,
|
||||||
SelectUserArticleForm,
|
RechargeForm,
|
||||||
SelectClubArticleForm,
|
CustomInvoiceForm
|
||||||
RechargeForm
|
|
||||||
)
|
)
|
||||||
from .tex import render_invoice
|
from .tex import render_invoice
|
||||||
from .payment_methods.forms import payment_method_factory
|
from .payment_methods.forms import payment_method_factory
|
||||||
from .utils import find_payment_method
|
from .utils import find_payment_method, send_mail_invoice
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -102,15 +109,9 @@ def new_facture(request, user, userid):
|
||||||
creation=True
|
creation=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if request.user.is_class_club:
|
article_formset = formset_factory(SelectArticleForm)(
|
||||||
article_formset = formset_factory(SelectClubArticleForm)(
|
|
||||||
request.POST or None,
|
request.POST or None,
|
||||||
form_kwargs={'user': request.user}
|
form_kwargs={'user': request.user, 'target_user': user}
|
||||||
)
|
|
||||||
else:
|
|
||||||
article_formset = formset_factory(SelectUserArticleForm)(
|
|
||||||
request.POST or None,
|
|
||||||
form_kwargs={'user': request.user}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if invoice_form.is_valid() and article_formset.is_valid():
|
if invoice_form.is_valid() and article_formset.is_valid():
|
||||||
|
@ -147,6 +148,8 @@ def new_facture(request, user, userid):
|
||||||
p.facture = new_invoice_instance
|
p.facture = new_invoice_instance
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
|
send_mail_invoice(new_invoice_instance)
|
||||||
|
|
||||||
return new_invoice_instance.paiement.end_payment(
|
return new_invoice_instance.paiement.end_payment(
|
||||||
new_invoice_instance,
|
new_invoice_instance,
|
||||||
request
|
request
|
||||||
|
@ -161,6 +164,7 @@ def new_facture(request, user, userid):
|
||||||
balance = user.solde
|
balance = user.solde
|
||||||
else:
|
else:
|
||||||
balance = None
|
balance = None
|
||||||
|
|
||||||
return form(
|
return form(
|
||||||
{
|
{
|
||||||
'factureform': invoice_form,
|
'factureform': invoice_form,
|
||||||
|
@ -175,10 +179,10 @@ def new_facture(request, user, userid):
|
||||||
|
|
||||||
# TODO : change facture to invoice
|
# TODO : change facture to invoice
|
||||||
@login_required
|
@login_required
|
||||||
@can_change(Facture, 'pdf')
|
@can_create(CustomInvoice)
|
||||||
def new_facture_pdf(request):
|
def new_custom_invoice(request):
|
||||||
"""
|
"""
|
||||||
View used to generate a custom PDF invoice. It's mainly used to
|
View used to generate a custom invoice. It's mainly used to
|
||||||
get invoices that are not taken into account, for the administrative
|
get invoices that are not taken into account, for the administrative
|
||||||
point of view.
|
point of view.
|
||||||
"""
|
"""
|
||||||
|
@ -187,56 +191,39 @@ def new_facture_pdf(request):
|
||||||
Q(type_user='All') | Q(type_user=request.user.class_name)
|
Q(type_user='All') | Q(type_user=request.user.class_name)
|
||||||
)
|
)
|
||||||
# Building the invocie form and the article formset
|
# Building the invocie form and the article formset
|
||||||
invoice_form = NewFactureFormPdf(request.POST or None)
|
invoice_form = CustomInvoiceForm(request.POST or None)
|
||||||
if request.user.is_class_club:
|
|
||||||
articles_formset = formset_factory(SelectClubArticleForm)(
|
article_formset = formset_factory(SelectArticleForm)(
|
||||||
request.POST or None,
|
request.POST or None,
|
||||||
form_kwargs={'user': request.user}
|
form_kwargs={'user': request.user, 'target_user': user}
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
articles_formset = formset_factory(SelectUserArticleForm)(
|
if invoice_form.is_valid() and articles_formset.is_valid():
|
||||||
request.POST or None,
|
new_invoice_instance = invoice_form.save()
|
||||||
form_kwargs={'user': request.user}
|
for art_item in articles_formset:
|
||||||
)
|
if art_item.cleaned_data:
|
||||||
if invoice_form.is_valid() and articles_formset.is_valid():
|
article = art_item.cleaned_data['article']
|
||||||
# Get the article list and build an list out of it
|
quantity = art_item.cleaned_data['quantity']
|
||||||
# contiaining (article_name, article_price, quantity, total_price)
|
Vente.objects.create(
|
||||||
articles_info = []
|
facture=new_invoice_instance,
|
||||||
for articles_form in articles_formset:
|
name=article.name,
|
||||||
if articles_form.cleaned_data:
|
prix=article.prix,
|
||||||
article = articles_form.cleaned_data['article']
|
type_cotisation=article.type_cotisation,
|
||||||
quantity = articles_form.cleaned_data['quantity']
|
duration=article.duration,
|
||||||
articles_info.append({
|
number=quantity
|
||||||
'name': article.name,
|
)
|
||||||
'price': article.prix,
|
messages.success(
|
||||||
'quantity': quantity,
|
request,
|
||||||
'total_price': article.prix * quantity
|
_("The custom invoice was created.")
|
||||||
})
|
)
|
||||||
paid = invoice_form.cleaned_data['paid']
|
return redirect(reverse('cotisations:index-custom-invoice'))
|
||||||
recipient = invoice_form.cleaned_data['dest']
|
|
||||||
address = invoice_form.cleaned_data['chambre']
|
|
||||||
total_price = sum(a['total_price'] for a in articles_info)
|
|
||||||
|
|
||||||
return render_invoice(request, {
|
|
||||||
'DATE': timezone.now(),
|
|
||||||
'recipient_name': recipient,
|
|
||||||
'address': address,
|
|
||||||
'article': articles_info,
|
|
||||||
'total': total_price,
|
|
||||||
'paid': paid,
|
|
||||||
'asso_name': AssoOption.get_cached_value('name'),
|
|
||||||
'line1': AssoOption.get_cached_value('adresse1'),
|
|
||||||
'line2': AssoOption.get_cached_value('adresse2'),
|
|
||||||
'siret': AssoOption.get_cached_value('siret'),
|
|
||||||
'email': AssoOption.get_cached_value('contact'),
|
|
||||||
'phone': AssoOption.get_cached_value('telephone'),
|
|
||||||
'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
|
|
||||||
})
|
|
||||||
return form({
|
return form({
|
||||||
'factureform': invoice_form,
|
'factureform': invoice_form,
|
||||||
'action_name': _("Create"),
|
'action_name': _("Create"),
|
||||||
'articlesformset': articles_formset,
|
'articlesformset': articles_formset,
|
||||||
'articles': articles
|
'articlelist': articles
|
||||||
}, 'cotisations/facture.html', request)
|
}, 'cotisations/facture.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,7 +276,7 @@ def facture_pdf(request, facture, **_kwargs):
|
||||||
def edit_facture(request, facture, **_kwargs):
|
def edit_facture(request, facture, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to edit an existing invoice.
|
View used to edit an existing invoice.
|
||||||
Articles can be added or remove to the invoice and quantity
|
Articles can be added or removed to the invoice and quantity
|
||||||
can be set as desired. This is also the view used to invalidate
|
can be set as desired. This is also the view used to invalidate
|
||||||
an invoice.
|
an invoice.
|
||||||
"""
|
"""
|
||||||
|
@ -315,7 +302,7 @@ def edit_facture(request, facture, **_kwargs):
|
||||||
purchase_form.save()
|
purchase_form.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The invoice has been successfully edited.")
|
_("The invoice was edited.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index'))
|
return redirect(reverse('cotisations:index'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -335,7 +322,7 @@ def del_facture(request, facture, **_kwargs):
|
||||||
facture.delete()
|
facture.delete()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The invoice has been successfully deleted.")
|
_("The invoice was deleted.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index'))
|
return redirect(reverse('cotisations:index'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -344,6 +331,100 @@ def del_facture(request, facture, **_kwargs):
|
||||||
}, 'cotisations/delete.html', request)
|
}, 'cotisations/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_edit(CustomInvoice)
|
||||||
|
def edit_custom_invoice(request, invoice, **kwargs):
|
||||||
|
# Building the invocie form and the article formset
|
||||||
|
invoice_form = CustomInvoiceForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=invoice
|
||||||
|
)
|
||||||
|
purchases_objects = Vente.objects.filter(facture=invoice)
|
||||||
|
purchase_form_set = modelformset_factory(
|
||||||
|
Vente,
|
||||||
|
fields=('name', 'number'),
|
||||||
|
extra=0,
|
||||||
|
max_num=len(purchases_objects)
|
||||||
|
)
|
||||||
|
purchase_form = purchase_form_set(
|
||||||
|
request.POST or None,
|
||||||
|
queryset=purchases_objects
|
||||||
|
)
|
||||||
|
if invoice_form.is_valid() and purchase_form.is_valid():
|
||||||
|
if invoice_form.changed_data:
|
||||||
|
invoice_form.save()
|
||||||
|
purchase_form.save()
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
_("The invoice was edited.")
|
||||||
|
)
|
||||||
|
return redirect(reverse('cotisations:index-custom-invoice'))
|
||||||
|
|
||||||
|
return form({
|
||||||
|
'factureform': invoice_form,
|
||||||
|
'venteform': purchase_form
|
||||||
|
}, 'cotisations/edit_facture.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_view(CustomInvoice)
|
||||||
|
def custom_invoice_pdf(request, invoice, **_kwargs):
|
||||||
|
"""
|
||||||
|
View used to generate a PDF file from an existing invoice in database
|
||||||
|
Creates a line for each Purchase (thus article sold) and generate the
|
||||||
|
invoice with the total price, the payment method, the address and the
|
||||||
|
legal information for the user.
|
||||||
|
"""
|
||||||
|
# TODO : change vente to purchase
|
||||||
|
purchases_objects = Vente.objects.all().filter(facture=invoice)
|
||||||
|
# Get the article list and build an list out of it
|
||||||
|
# contiaining (article_name, article_price, quantity, total_price)
|
||||||
|
purchases_info = []
|
||||||
|
for purchase in purchases_objects:
|
||||||
|
purchases_info.append({
|
||||||
|
'name': purchase.name,
|
||||||
|
'price': purchase.prix,
|
||||||
|
'quantity': purchase.number,
|
||||||
|
'total_price': purchase.prix_total
|
||||||
|
})
|
||||||
|
return render_invoice(request, {
|
||||||
|
'paid': invoice.paid,
|
||||||
|
'fid': invoice.id,
|
||||||
|
'DATE': invoice.date,
|
||||||
|
'recipient_name': invoice.recipient,
|
||||||
|
'address': invoice.address,
|
||||||
|
'article': purchases_info,
|
||||||
|
'total': invoice.prix_total(),
|
||||||
|
'asso_name': AssoOption.get_cached_value('name'),
|
||||||
|
'line1': AssoOption.get_cached_value('adresse1'),
|
||||||
|
'line2': AssoOption.get_cached_value('adresse2'),
|
||||||
|
'siret': AssoOption.get_cached_value('siret'),
|
||||||
|
'email': AssoOption.get_cached_value('contact'),
|
||||||
|
'phone': AssoOption.get_cached_value('telephone'),
|
||||||
|
'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# TODO : change facture to invoice
|
||||||
|
@login_required
|
||||||
|
@can_delete(CustomInvoice)
|
||||||
|
def del_custom_invoice(request, invoice, **_kwargs):
|
||||||
|
"""
|
||||||
|
View used to delete an existing invocie.
|
||||||
|
"""
|
||||||
|
if request.method == "POST":
|
||||||
|
invoice.delete()
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
_("The invoice was deleted.")
|
||||||
|
)
|
||||||
|
return redirect(reverse('cotisations:index-custom-invoice'))
|
||||||
|
return form({
|
||||||
|
'objet': invoice,
|
||||||
|
'objet_name': _("Invoice")
|
||||||
|
}, 'cotisations/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_create(Article)
|
@can_create(Article)
|
||||||
def add_article(request):
|
def add_article(request):
|
||||||
|
@ -361,7 +442,7 @@ def add_article(request):
|
||||||
article.save()
|
article.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The article has been successfully created.")
|
_("The article was created.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-article'))
|
return redirect(reverse('cotisations:index-article'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -383,7 +464,7 @@ def edit_article(request, article_instance, **_kwargs):
|
||||||
article.save()
|
article.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The article has been successfully edited.")
|
_("The article was edited.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-article'))
|
return redirect(reverse('cotisations:index-article'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -405,7 +486,7 @@ def del_article(request, instances):
|
||||||
article_del.delete()
|
article_del.delete()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The article(s) have been successfully deleted.")
|
_("The articles were deleted.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-article'))
|
return redirect(reverse('cotisations:index-article'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -433,7 +514,7 @@ def add_paiement(request):
|
||||||
payment_method.save(payment)
|
payment_method.save(payment)
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The payment method has been successfully created.")
|
_("The payment method was created.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-paiement'))
|
return redirect(reverse('cotisations:index-paiement'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -469,8 +550,7 @@ def edit_paiement(request, paiement_instance, **_kwargs):
|
||||||
if payment_method is not None:
|
if payment_method is not None:
|
||||||
payment_method.save()
|
payment_method.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,_("The payment method was edited.")
|
||||||
_("The payement method has been successfully edited.")
|
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-paiement'))
|
return redirect(reverse('cotisations:index-paiement'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -496,8 +576,7 @@ def del_paiement(request, instances):
|
||||||
payment_del.delete()
|
payment_del.delete()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The payment method %(method_name)s has been \
|
_("The payment method %(method_name)s was deleted.") % {
|
||||||
successfully deleted.") % {
|
|
||||||
'method_name': payment_del
|
'method_name': payment_del
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -529,7 +608,7 @@ def add_banque(request):
|
||||||
bank.save()
|
bank.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The bank has been successfully created.")
|
_("The bank was created.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-banque'))
|
return redirect(reverse('cotisations:index-banque'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -552,7 +631,7 @@ def edit_banque(request, banque_instance, **_kwargs):
|
||||||
bank.save()
|
bank.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The bank has been successfully edited")
|
_("The bank was edited.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('cotisations:index-banque'))
|
return redirect(reverse('cotisations:index-banque'))
|
||||||
return form({
|
return form({
|
||||||
|
@ -577,8 +656,7 @@ def del_banque(request, instances):
|
||||||
bank_del.delete()
|
bank_del.delete()
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("The bank %(bank_name)s has been successfully \
|
_("The bank %(bank_name)s was deleted.") % {
|
||||||
deleted.") % {
|
|
||||||
'bank_name': bank_del
|
'bank_name': bank_del
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -678,8 +756,31 @@ def index_banque(request):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_view_all(CustomInvoice)
|
||||||
|
def index_custom_invoice(request):
|
||||||
|
"""View used to display every custom invoice."""
|
||||||
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
|
custom_invoice_list = CustomInvoice.objects.prefetch_related('vente_set')
|
||||||
|
custom_invoice_list = SortTable.sort(
|
||||||
|
custom_invoice_list,
|
||||||
|
request.GET.get('col'),
|
||||||
|
request.GET.get('order'),
|
||||||
|
SortTable.COTISATIONS_CUSTOM
|
||||||
|
)
|
||||||
|
custom_invoice_list = re2o_paginator(
|
||||||
|
request,
|
||||||
|
custom_invoice_list,
|
||||||
|
pagination_number,
|
||||||
|
)
|
||||||
|
return render(request, 'cotisations/index_custom_invoice.html', {
|
||||||
|
'custom_invoice_list': custom_invoice_list
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_all(Facture)
|
@can_view_all(Facture)
|
||||||
|
@can_view_all(CustomInvoice)
|
||||||
def index(request):
|
def index(request):
|
||||||
"""
|
"""
|
||||||
View used to display the list of all exisitng invoices.
|
View used to display the list of all exisitng invoices.
|
||||||
|
@ -695,7 +796,7 @@ def index(request):
|
||||||
)
|
)
|
||||||
invoice_list = re2o_paginator(request, invoice_list, pagination_number)
|
invoice_list = re2o_paginator(request, invoice_list, pagination_number)
|
||||||
return render(request, 'cotisations/index.html', {
|
return render(request, 'cotisations/index.html', {
|
||||||
'facture_list': invoice_list
|
'facture_list': invoice_list,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -726,7 +827,7 @@ def credit_solde(request, user, **_kwargs):
|
||||||
kwargs={'userid': user.id}
|
kwargs={'userid': user.id}
|
||||||
))
|
))
|
||||||
|
|
||||||
refill_form = RechargeForm(request.POST or None, user=request.user)
|
refill_form = RechargeForm(request.POST or None, user=user, user_source=request.user)
|
||||||
if refill_form.is_valid():
|
if refill_form.is_valid():
|
||||||
price = refill_form.cleaned_data['value']
|
price = refill_form.cleaned_data['value']
|
||||||
invoice = Facture(user=user)
|
invoice = Facture(user=user)
|
||||||
|
@ -746,12 +847,16 @@ def credit_solde(request, user, **_kwargs):
|
||||||
prix=refill_form.cleaned_data['value'],
|
prix=refill_form.cleaned_data['value'],
|
||||||
number=1
|
number=1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
send_mail_invoice(invoice)
|
||||||
|
|
||||||
return invoice.paiement.end_payment(invoice, request)
|
return invoice.paiement.end_payment(invoice, request)
|
||||||
p = get_object_or_404(Paiement, is_balance=True)
|
p = get_object_or_404(Paiement, is_balance=True)
|
||||||
return form({
|
return form({
|
||||||
'factureform': refill_form,
|
'factureform': refill_form,
|
||||||
'balance': request.user.solde,
|
'balance': user.solde,
|
||||||
'title': _("Refill your balance"),
|
'title': _("Refill your balance"),
|
||||||
'action_name': _("Pay"),
|
'action_name': _("Pay"),
|
||||||
'max_balance': p.payment_method.maximum_balance,
|
'max_balance': p.payment_method.maximum_balance,
|
||||||
}, 'cotisations/facture.html', request)
|
}, 'cotisations/facture.html', request)
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ from preferences.models import OptionalTopologie
|
||||||
options, created = OptionalTopologie.objects.get_or_create()
|
options, created = OptionalTopologie.objects.get_or_create()
|
||||||
VLAN_NOK = options.vlan_decision_nok.vlan_id
|
VLAN_NOK = options.vlan_decision_nok.vlan_id
|
||||||
VLAN_OK = options.vlan_decision_ok.vlan_id
|
VLAN_OK = options.vlan_decision_ok.vlan_id
|
||||||
|
RADIUS_POLICY = options.radius_general_policy
|
||||||
|
|
||||||
#: Serveur radius de test (pas la prod)
|
#: Serveur radius de test (pas la prod)
|
||||||
TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False))
|
TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False))
|
||||||
|
@ -347,7 +348,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
if not nas_machine:
|
if not nas_machine:
|
||||||
return ('?', u'Chambre inconnue', u'Nas inconnu', VLAN_OK)
|
return ('?', u'Chambre inconnue', u'Nas inconnu', VLAN_OK)
|
||||||
|
|
||||||
sw_name = str(nas_machine)
|
sw_name = str(getattr(nas_machine, 'short_name', str(nas_machine)))
|
||||||
|
|
||||||
port = (Port.objects
|
port = (Port.objects
|
||||||
.filter(
|
.filter(
|
||||||
|
@ -355,27 +356,47 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
port=port_number
|
port=port_number
|
||||||
)
|
)
|
||||||
.first())
|
.first())
|
||||||
|
|
||||||
# Si le port est inconnu, on place sur le vlan defaut
|
# Si le port est inconnu, on place sur le vlan defaut
|
||||||
|
# Aucune information particulière ne permet de déterminer quelle
|
||||||
|
# politique à appliquer sur ce port
|
||||||
if not port:
|
if not port:
|
||||||
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
||||||
|
|
||||||
# Si un vlan a été précisé, on l'utilise pour VLAN_OK
|
# On récupère le profil du port
|
||||||
if port.vlan_force:
|
port_profile = port.get_port_profile
|
||||||
DECISION_VLAN = int(port.vlan_force.vlan_id)
|
|
||||||
|
# Si un vlan a été précisé dans la config du port,
|
||||||
|
# on l'utilise pour VLAN_OK
|
||||||
|
if port_profile.vlan_untagged:
|
||||||
|
DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id)
|
||||||
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
|
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
|
||||||
else:
|
else:
|
||||||
DECISION_VLAN = VLAN_OK
|
DECISION_VLAN = VLAN_OK
|
||||||
|
|
||||||
if port.radius == 'NO':
|
# Si le port est désactivé, on rejette sur le vlan de déconnexion
|
||||||
|
if not port.state:
|
||||||
|
return (sw_name, port.room, u'Port desactivé', VLAN_NOK)
|
||||||
|
|
||||||
|
# Si radius est désactivé, on laisse passer
|
||||||
|
if port_profile.radius_type == 'NO':
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
"",
|
"",
|
||||||
u"Pas d'authentification sur ce port" + extra_log,
|
u"Pas d'authentification sur ce port" + extra_log,
|
||||||
DECISION_VLAN)
|
DECISION_VLAN)
|
||||||
|
|
||||||
if port.radius == 'BLOQ':
|
# Si le 802.1X est activé sur ce port, cela veut dire que la personne a été accept précédemment
|
||||||
return (sw_name, port.room, u'Port desactive', VLAN_NOK)
|
# Par conséquent, on laisse passer sur le bon vlan
|
||||||
|
if nas_type.port_access_mode == '802.1X' and port_profile.radius_type == '802.1X':
|
||||||
|
room = port.room or "Chambre/local inconnu"
|
||||||
|
return (sw_name, room, u'Acceptation authentification 802.1X', DECISION_VLAN)
|
||||||
|
|
||||||
if port.radius == 'STRICT':
|
# Sinon, cela veut dire qu'on fait de l'auth radius par mac
|
||||||
|
# Si le port est en mode strict, on vérifie que tous les users
|
||||||
|
# rattachés à ce port sont bien à jour de cotisation. Sinon on rejette (anti squattage)
|
||||||
|
# Il n'est pas possible de se connecter sur une prise strict sans adhérent à jour de cotis
|
||||||
|
# dedans
|
||||||
|
if port_profile.radius_mode == 'STRICT':
|
||||||
room = port.room
|
room = port.room
|
||||||
if not room:
|
if not room:
|
||||||
return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK)
|
return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK)
|
||||||
|
@ -390,7 +411,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
return (sw_name, room, u'Chambre resident desactive', VLAN_NOK)
|
return (sw_name, room, u'Chambre resident desactive', VLAN_NOK)
|
||||||
# else: user OK, on passe à la verif MAC
|
# else: user OK, on passe à la verif MAC
|
||||||
|
|
||||||
if port.radius == 'COMMON' or port.radius == 'STRICT':
|
# Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd
|
||||||
|
if port_profile.radius_mode == 'COMMON' or port_profile.radius_mode == 'STRICT':
|
||||||
# Authentification par mac
|
# Authentification par mac
|
||||||
interface = (Interface.objects
|
interface = (Interface.objects
|
||||||
.filter(mac_address=mac_address)
|
.filter(mac_address=mac_address)
|
||||||
|
@ -399,15 +421,19 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
.first())
|
.first())
|
||||||
if not interface:
|
if not interface:
|
||||||
room = port.room
|
room = port.room
|
||||||
# On essaye de register la mac
|
# On essaye de register la mac, si l'autocapture a été activée
|
||||||
|
# Sinon on rejette sur vlan_nok
|
||||||
if not nas_type.autocapture_mac:
|
if not nas_type.autocapture_mac:
|
||||||
return (sw_name, "", u'Machine inconnue', VLAN_NOK)
|
return (sw_name, "", u'Machine inconnue', VLAN_NOK)
|
||||||
|
# On ne peut autocapturer que si on connait la chambre et donc l'user correspondant
|
||||||
elif not room:
|
elif not room:
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
"Inconnue",
|
"Inconnue",
|
||||||
u'Chambre et machine inconnues',
|
u'Chambre et machine inconnues',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
else:
|
else:
|
||||||
|
# Si la chambre est vide (local club, prises en libre services)
|
||||||
|
# Impossible d'autocapturer
|
||||||
if not room_user:
|
if not room_user:
|
||||||
room_user = User.objects.filter(
|
room_user = User.objects.filter(
|
||||||
Q(club__room=port.room) | Q(adherent__room=port.room)
|
Q(club__room=port.room) | Q(adherent__room=port.room)
|
||||||
|
@ -418,6 +444,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
u'Machine et propriétaire de la chambre '
|
u'Machine et propriétaire de la chambre '
|
||||||
'inconnus',
|
'inconnus',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
|
# Si il y a plus d'un user dans la chambre, impossible de savoir à qui
|
||||||
|
# Ajouter la machine
|
||||||
elif room_user.count() > 1:
|
elif room_user.count() > 1:
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
room,
|
room,
|
||||||
|
@ -425,19 +453,24 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
'dans la chambre/local -> ajout de mac '
|
'dans la chambre/local -> ajout de mac '
|
||||||
'automatique impossible',
|
'automatique impossible',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
|
# Si l'adhérent de la chambre n'est pas à jour de cotis, pas d'autocapture
|
||||||
elif not room_user.first().has_access():
|
elif not room_user.first().has_access():
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
room,
|
room,
|
||||||
u'Machine inconnue et adhérent non cotisant',
|
u'Machine inconnue et adhérent non cotisant',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
|
# Sinon on capture et on laisse passer sur le bon vlan
|
||||||
else:
|
else:
|
||||||
result, reason = (room_user
|
interface, reason = (room_user
|
||||||
.first()
|
.first()
|
||||||
.autoregister_machine(
|
.autoregister_machine(
|
||||||
mac_address,
|
mac_address,
|
||||||
nas_type
|
nas_type
|
||||||
))
|
))
|
||||||
if result:
|
if interface:
|
||||||
|
## Si on choisi de placer les machines sur le vlan correspondant à leur type :
|
||||||
|
if RADIUS_POLICY == 'MACHINE':
|
||||||
|
DECISION_VLAN = interface.type.ip_type.vlan.vlan_id
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
room,
|
room,
|
||||||
u'Access Ok, Capture de la mac: ' + extra_log,
|
u'Access Ok, Capture de la mac: ' + extra_log,
|
||||||
|
@ -449,6 +482,9 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
reason + str(mac_address)
|
reason + str(mac_address)
|
||||||
),
|
),
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
|
# L'interface a été trouvée, on vérifie qu'elle est active, sinon on reject
|
||||||
|
# Si elle n'a pas d'ipv4, on lui en met une
|
||||||
|
# Enfin on laisse passer sur le vlan pertinent
|
||||||
else:
|
else:
|
||||||
room = port.room
|
room = port.room
|
||||||
if not interface.is_active:
|
if not interface.is_active:
|
||||||
|
@ -456,7 +492,10 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u'Machine non active / adherent non cotisant',
|
u'Machine non active / adherent non cotisant',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
elif not interface.ipv4:
|
## Si on choisi de placer les machines sur le vlan correspondant à leur type :
|
||||||
|
if RADIUS_POLICY == 'MACHINE':
|
||||||
|
DECISION_VLAN = interface.type.ip_type.vlan.vlan_id
|
||||||
|
if not interface.ipv4:
|
||||||
interface.assign_ipv4()
|
interface.assign_ipv4()
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
room,
|
room,
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
python re2o {
|
python re2o {
|
||||||
module = auth
|
module = auth
|
||||||
|
|
||||||
python_path = /etc/freeradius/3.0:/usr/lib/python2.7/:/usr/lib/python2.7/dist-packages/:/usr/local/lib/python2.7/site-packages/:/usr/local/lib/python2.7/dist-packages/
|
python_path = /etc/freeradius/3.0:/usr/lib/python2.7:/usr/lib/python2.7/dist-packages:/usr/local/lib/python2.7/site-packages:/usr/lib/python2.7/lib-dynload:/usr/local/lib/python2.7/dist-packages
|
||||||
|
|
||||||
mod_instantiate = ${.module}
|
mod_instantiate = ${.module}
|
||||||
func_instantiate = instantiate
|
func_instantiate = instantiate
|
||||||
|
|
|
@ -1157,6 +1157,7 @@ olcDbIndex: dc eq
|
||||||
olcDbIndex: entryCSN eq
|
olcDbIndex: entryCSN eq
|
||||||
olcDbIndex: entryUUID eq
|
olcDbIndex: entryUUID eq
|
||||||
olcDbIndex: radiusCallingStationId eq
|
olcDbIndex: radiusCallingStationId eq
|
||||||
|
olcSizeLimit: 50000
|
||||||
structuralObjectClass: olcHdbConfig
|
structuralObjectClass: olcHdbConfig
|
||||||
entryUUID: fc8fa138-514b-1034-9c36-0faf5bc7ead5
|
entryUUID: fc8fa138-514b-1034-9c36-0faf5bc7ead5
|
||||||
creatorsName: cn=admin,cn=config
|
creatorsName: cn=admin,cn=config
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
|
@ -38,4 +39,6 @@ def can_view(user):
|
||||||
viewing is granted and msg is a message (can be None).
|
viewing is granted and msg is a message (can be None).
|
||||||
"""
|
"""
|
||||||
can = user.has_module_perms('admin')
|
can = user.has_module_perms('admin')
|
||||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
return can, None if can else _("You don't have the right to view this"
|
||||||
|
" application.")
|
||||||
|
|
||||||
|
|
BIN
logs/locale/fr/LC_MESSAGES/django.mo
Normal file
BIN
logs/locale/fr/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
338
logs/locale/fr/LC_MESSAGES/django.po
Normal file
338
logs/locale/fr/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: 2.5\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2018-08-15 20:12+0200\n"
|
||||||
|
"PO-Revision-Date: 2018-06-23 16:01+0200\n"
|
||||||
|
"Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"Language: fr_FR\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: acl.py:42
|
||||||
|
msgid "You don't have the right to view this application."
|
||||||
|
msgstr "Vous n'avez pas le droit de voir cette application."
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_logs.html:36
|
||||||
|
msgid "Edited object"
|
||||||
|
msgstr "Objet modifié"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_logs.html:37
|
||||||
|
#: templates/logs/aff_stats_models.html:32
|
||||||
|
msgid "Object type"
|
||||||
|
msgstr "Type d'objet"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_logs.html:38
|
||||||
|
msgid "Edited by"
|
||||||
|
msgstr "Modifié par"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_logs.html:40
|
||||||
|
msgid "Date of editing"
|
||||||
|
msgstr "Date de modification"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_logs.html:42
|
||||||
|
msgid "Comment"
|
||||||
|
msgstr "Commentaire"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_logs.html:58 templates/logs/aff_summary.html:62
|
||||||
|
#: templates/logs/aff_summary.html:85 templates/logs/aff_summary.html:104
|
||||||
|
#: templates/logs/aff_summary.html:123 templates/logs/aff_summary.html:142
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Annuler"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_models.html:29
|
||||||
|
#, python-format
|
||||||
|
msgid "Statistics of the set %(key)s"
|
||||||
|
msgstr "Statistiques de l'ensemble %(key)s"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_models.html:33
|
||||||
|
msgid "Number of stored entries"
|
||||||
|
msgstr "Nombre d'entrées enregistrées"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_users.html:31
|
||||||
|
#, python-format
|
||||||
|
msgid "Statistics per %(key_dict)s of %(key)s"
|
||||||
|
msgstr "Statistiques par %(key_dict)s de %(key)s"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_users.html:34
|
||||||
|
#, python-format
|
||||||
|
msgid "Number of %(key)s per %(key_dict)s"
|
||||||
|
msgstr "Nombre de %(key)s par %(key_dict)s"
|
||||||
|
|
||||||
|
#: templates/logs/aff_stats_users.html:35
|
||||||
|
msgid "Rank"
|
||||||
|
msgstr "Rang"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:37
|
||||||
|
msgid "Date"
|
||||||
|
msgstr "Date"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:39
|
||||||
|
msgid "Editing"
|
||||||
|
msgstr "Modification"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:48
|
||||||
|
#, python-format
|
||||||
|
msgid "%(username)s has banned"
|
||||||
|
msgstr "%(username)s a banni"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:52 templates/logs/aff_summary.html:75
|
||||||
|
msgid "No reason"
|
||||||
|
msgstr "Aucun motif"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:71
|
||||||
|
#, python-format
|
||||||
|
msgid "%(username)s has graciously authorised"
|
||||||
|
msgstr "%(username)s a autorisé gracieusement"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:94
|
||||||
|
#, python-format
|
||||||
|
msgid "%(username)s has updated"
|
||||||
|
msgstr "%(username)s a mis à jour"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:113
|
||||||
|
#, python-format
|
||||||
|
msgid "%(username)s has sold %(number)sx %(name)s to"
|
||||||
|
msgstr "%(username)s a vendu %(number)sx %(name)s à"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:116
|
||||||
|
#, python-format
|
||||||
|
msgid "+%(duration)s months"
|
||||||
|
msgstr "+%(duration)s mois"
|
||||||
|
|
||||||
|
#: templates/logs/aff_summary.html:132
|
||||||
|
#, python-format
|
||||||
|
msgid "%(username)s has edited an interface of"
|
||||||
|
msgstr "%(username)s a modifié une interface de"
|
||||||
|
|
||||||
|
#: templates/logs/delete.html:29
|
||||||
|
msgid "Deletion of actions"
|
||||||
|
msgstr "Suppression d'actions"
|
||||||
|
|
||||||
|
#: templates/logs/delete.html:35
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Warning: are you sure you want to delete this action %(objet_name)s "
|
||||||
|
"( %(objet)s )?"
|
||||||
|
msgstr ""
|
||||||
|
"Attention: voulez-vous vraiment supprimer cette action %(objet_name)s "
|
||||||
|
"( %(objet)s ) ?"
|
||||||
|
|
||||||
|
#: templates/logs/delete.html:36
|
||||||
|
msgid "Confirm"
|
||||||
|
msgstr "Confirmer"
|
||||||
|
|
||||||
|
#: templates/logs/index.html:29 templates/logs/stats_general.html:29
|
||||||
|
#: templates/logs/stats_logs.html:29 templates/logs/stats_models.html:29
|
||||||
|
#: templates/logs/stats_users.html:29
|
||||||
|
msgid "Statistics"
|
||||||
|
msgstr "Statistiques"
|
||||||
|
|
||||||
|
#: templates/logs/index.html:32 templates/logs/stats_logs.html:32 views.py:403
|
||||||
|
msgid "Actions performed"
|
||||||
|
msgstr "Actions effectuées"
|
||||||
|
|
||||||
|
#: templates/logs/sidebar.html:33
|
||||||
|
msgid "Summary"
|
||||||
|
msgstr "Résumé"
|
||||||
|
|
||||||
|
#: templates/logs/sidebar.html:37
|
||||||
|
msgid "Events"
|
||||||
|
msgstr "Évènements"
|
||||||
|
|
||||||
|
#: templates/logs/sidebar.html:41
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Général"
|
||||||
|
|
||||||
|
#: templates/logs/sidebar.html:45
|
||||||
|
msgid "Database"
|
||||||
|
msgstr "Base de données"
|
||||||
|
|
||||||
|
#: templates/logs/sidebar.html:49
|
||||||
|
msgid "Wiring actions"
|
||||||
|
msgstr "Actions de câblage"
|
||||||
|
|
||||||
|
#: templates/logs/sidebar.html:53 views.py:325
|
||||||
|
msgid "Users"
|
||||||
|
msgstr "Utilisateurs"
|
||||||
|
|
||||||
|
#: templates/logs/stats_general.html:32
|
||||||
|
msgid "General statistics"
|
||||||
|
msgstr "Statistiques générales"
|
||||||
|
|
||||||
|
#: templates/logs/stats_models.html:32
|
||||||
|
msgid "Database statistics"
|
||||||
|
msgstr "Statistiques sur la base de données"
|
||||||
|
|
||||||
|
#: templates/logs/stats_users.html:32
|
||||||
|
msgid "Statistics about users"
|
||||||
|
msgstr "Statistiques sur les utilisateurs"
|
||||||
|
|
||||||
|
#: views.py:191
|
||||||
|
msgid "Nonexistent revision."
|
||||||
|
msgstr "Révision inexistante."
|
||||||
|
|
||||||
|
#: views.py:194
|
||||||
|
msgid "The action was deleted."
|
||||||
|
msgstr "L'action a été supprimée."
|
||||||
|
|
||||||
|
#: views.py:227
|
||||||
|
msgid "Category"
|
||||||
|
msgstr "Catégorie"
|
||||||
|
|
||||||
|
#: views.py:228
|
||||||
|
msgid "Number of users (members and clubs)"
|
||||||
|
msgstr "Nombre d'utilisateurs (adhérents et clubs)"
|
||||||
|
|
||||||
|
#: views.py:229
|
||||||
|
msgid "Number of members"
|
||||||
|
msgstr "Nombre d'adhérents"
|
||||||
|
|
||||||
|
#: views.py:230
|
||||||
|
msgid "Number of clubs"
|
||||||
|
msgstr "Nombre de clubs"
|
||||||
|
|
||||||
|
#: views.py:234
|
||||||
|
msgid "Activated users"
|
||||||
|
msgstr "Utilisateurs activés"
|
||||||
|
|
||||||
|
#: views.py:242
|
||||||
|
msgid "Disabled users"
|
||||||
|
msgstr "Utilisateurs désactivés"
|
||||||
|
|
||||||
|
#: views.py:250
|
||||||
|
msgid "Archived users"
|
||||||
|
msgstr "Utilisateurs archivés"
|
||||||
|
|
||||||
|
#: views.py:258
|
||||||
|
msgid "Contributing members"
|
||||||
|
msgstr "Adhérents cotisants"
|
||||||
|
|
||||||
|
#: views.py:264
|
||||||
|
msgid "Users benefiting from a connection"
|
||||||
|
msgstr "Utilisateurs bénéficiant d'une connexion"
|
||||||
|
|
||||||
|
#: views.py:270
|
||||||
|
msgid "Banned users"
|
||||||
|
msgstr "Utilisateurs bannis"
|
||||||
|
|
||||||
|
#: views.py:276
|
||||||
|
msgid "Users benefiting from a free connection"
|
||||||
|
msgstr "Utilisateurs bénéficiant d'une connexion gratuite"
|
||||||
|
|
||||||
|
#: views.py:282
|
||||||
|
msgid "Active interfaces (with access to the network)"
|
||||||
|
msgstr "Interfaces actives (ayant accès au réseau)"
|
||||||
|
|
||||||
|
#: views.py:292
|
||||||
|
msgid "Active interfaces assigned IPv4"
|
||||||
|
msgstr "Interfaces actives assignées IPv4"
|
||||||
|
|
||||||
|
#: views.py:305
|
||||||
|
msgid "IP range"
|
||||||
|
msgstr "Plage d'IP"
|
||||||
|
|
||||||
|
#: views.py:306
|
||||||
|
msgid "VLAN"
|
||||||
|
msgstr "VLAN"
|
||||||
|
|
||||||
|
#: views.py:307
|
||||||
|
msgid "Total number of IP addresses"
|
||||||
|
msgstr "Nombre total d'adresses IP"
|
||||||
|
|
||||||
|
#: views.py:308
|
||||||
|
msgid "Number of assigned IP addresses"
|
||||||
|
msgstr "Nombre d'adresses IP non assignées"
|
||||||
|
|
||||||
|
#: views.py:309
|
||||||
|
msgid "Number of IP address assigned to an activated machine"
|
||||||
|
msgstr "Nombre d'adresses IP assignées à une machine activée"
|
||||||
|
|
||||||
|
#: views.py:310
|
||||||
|
msgid "Number of nonassigned IP addresses"
|
||||||
|
msgstr "Nombre d'adresses IP non assignées"
|
||||||
|
|
||||||
|
#: views.py:337
|
||||||
|
msgid "Subscriptions"
|
||||||
|
msgstr "Cotisations"
|
||||||
|
|
||||||
|
#: views.py:359 views.py:420
|
||||||
|
msgid "Machines"
|
||||||
|
msgstr "Machines"
|
||||||
|
|
||||||
|
#: views.py:386
|
||||||
|
msgid "Topology"
|
||||||
|
msgstr "Topologie"
|
||||||
|
|
||||||
|
#: views.py:405
|
||||||
|
msgid "Number of actions"
|
||||||
|
msgstr "Nombre d'actions"
|
||||||
|
|
||||||
|
#: views.py:419 views.py:437 views.py:442 views.py:447 views.py:462
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Utilisateur"
|
||||||
|
|
||||||
|
#: views.py:423
|
||||||
|
msgid "Invoice"
|
||||||
|
msgstr "Facture"
|
||||||
|
|
||||||
|
#: views.py:426
|
||||||
|
msgid "Ban"
|
||||||
|
msgstr "Bannissement"
|
||||||
|
|
||||||
|
#: views.py:429
|
||||||
|
msgid "Whitelist"
|
||||||
|
msgstr "Accès gracieux"
|
||||||
|
|
||||||
|
#: views.py:432
|
||||||
|
msgid "Rights"
|
||||||
|
msgstr "Droits"
|
||||||
|
|
||||||
|
#: views.py:436
|
||||||
|
msgid "School"
|
||||||
|
msgstr "Établissement"
|
||||||
|
|
||||||
|
#: views.py:441
|
||||||
|
msgid "Payment method"
|
||||||
|
msgstr "Moyen de paiement"
|
||||||
|
|
||||||
|
#: views.py:446
|
||||||
|
msgid "Bank"
|
||||||
|
msgstr "Banque"
|
||||||
|
|
||||||
|
#: views.py:463
|
||||||
|
msgid "Action"
|
||||||
|
msgstr "Action"
|
||||||
|
|
||||||
|
#: views.py:494
|
||||||
|
msgid "No model found."
|
||||||
|
msgstr "Aucun modèle trouvé."
|
||||||
|
|
||||||
|
#: views.py:500
|
||||||
|
msgid "Nonexistent entry."
|
||||||
|
msgstr "Entrée inexistante."
|
||||||
|
|
||||||
|
#: views.py:507
|
||||||
|
msgid "You don't have the right to access this menu."
|
||||||
|
msgstr "Vous n'avez pas le droit d'accéder à ce menu."
|
|
@ -40,3 +40,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Objet modifié</th>
|
<th>{% trans "Edited object" %}</th>
|
||||||
<th>Type de l'objet</th>
|
<th>{% trans "Object type" %}</th>
|
||||||
<th>{% include "buttons/sort.html" with prefix='logs' col='author' text='Modification par' %}</th>
|
{% trans "Edited by" as tr_edited_by %}
|
||||||
<th>{% include "buttons/sort.html" with prefix='logs' col='date' text='Date de modification' %}</th>
|
<th>{% include "buttons/sort.html" with prefix='logs' col='author' text=tr_edited_by %}</th>
|
||||||
<th>Commentaire</th>
|
{% trans "Date of editing" as tr_date_of_editing %}
|
||||||
|
<th>{% include "buttons/sort.html" with prefix='logs' col='date' text=tr_date_of_editing %}</th>
|
||||||
|
<th>{% trans "Comment" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -52,7 +55,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
Annuler
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -64,3 +67,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% if revisions_list.paginator %}
|
{% if revisions_list.paginator %}
|
||||||
{% include "pagination.html" with list=revisions_list %}
|
{% include "pagination.html" with list=revisions_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% for key, stats in stats_list.items %}
|
{% for key, stats in stats_list.items %}
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<h4>Statistiques de l'ensemble {{ key }}</h4>
|
<h4>{% blocktrans %}Statistics of the set {{ key }}{% endblocktrans %}</h4>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type d'objet</th>
|
<th>{% trans "Object type" %}</th>
|
||||||
<th>Nombre d'entrée stockées</th>
|
<th>{% trans "Number of stored entries" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% for key, stat in stats.items %}
|
{% for key, stat in stats.items %}
|
||||||
|
@ -39,3 +41,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% for key_dict, stats_dict in stats_list.items %}
|
{% for key_dict, stats_dict in stats_list.items %}
|
||||||
{% for key, stats in stats_dict.items %}
|
{% for key, stats in stats_dict.items %}
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<h4>Statistiques par {{ key_dict }} de {{ key }}</h4>
|
<h4>{% blocktrans %}Statistics per {{ key_dict }} of {{ key }}{% endblocktrans %}</h4>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ key_dict }}</th>
|
<th>{{ key_dict }}</th>
|
||||||
<th>Nombre de {{ key }} par {{ key_dict }}</th>
|
<th>{% blocktrans %}Number of {{ key }} per {{ key_dict }}{% endblocktrans %}</th>
|
||||||
<th>Rang</th>
|
<th>{% trans "Rank" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% for stat in stats %}
|
{% for stat in stats %}
|
||||||
|
@ -43,3 +45,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</table>
|
</table>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% include "buttons/sort.html" with prefix='sum' col='date' text='Date' %}</th>
|
{% trans "Date" as tr_date %}
|
||||||
<th>Modification</th>
|
<th>{% include "buttons/sort.html" with prefix='sum' col='date' text=tr_date %}</th>
|
||||||
|
<th>{% trans "Editing" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -41,11 +45,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr class="danger">
|
<tr class="danger">
|
||||||
<td>{{ v.datetime }}</td>
|
<td>{{ v.datetime }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ v.username }} a banni
|
{% blocktrans with username=v.username %}{{ username }} has banned{% endblocktrans %}
|
||||||
<a href="{% url 'users:profil' v.version.object.user_id %}">{{ v.version.object.user.get_username }}</a>
|
<a href="{% url 'users:profil' v.version.object.user_id %}">{{ v.version.object.user.get_username }}</a>
|
||||||
(<i>
|
(<i>
|
||||||
{% if v.version.object.raison == '' %}
|
{% if v.version.object.raison == '' %}
|
||||||
Aucune raison
|
{% trans "No reason" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ v.version.object.raison }}
|
{{ v.version.object.raison }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -55,7 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
Annuler
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -64,11 +68,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr class="success">
|
<tr class="success">
|
||||||
<td>{{ v.datetime }}</td>
|
<td>{{ v.datetime }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ v.username }} a autorisé gracieusement
|
{% blocktrans with username=v.username %}{{ username }} has graciously authorised{% endblocktrans %}
|
||||||
<a href="{% url 'users:profil' v.version.object.user_id %}">{{ v.version.object.user.get_username }}</a>
|
<a href="{% url 'users:profil' v.version.object.user_id %}">{{ v.version.object.user.get_username }}</a>
|
||||||
(<i>
|
(<i>
|
||||||
{% if v.version.object.raison == '' %}
|
{% if v.version.object.raison == '' %}
|
||||||
Aucune raison
|
{% trans "No reason" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ v.version.object.raison }}
|
{{ v.version.object.raison }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -78,7 +82,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
Annuler
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -87,7 +91,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ v.datetime }}</td>
|
<td>{{ v.datetime }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ v.username }} a mis à jour
|
{% blocktrans with username=v.username %}{{ username }} has updated{% endblocktrans %}
|
||||||
<a href="{% url 'users:profil' v.version.object.id %}">{{ v.version.object.get_username }}</a>
|
<a href="{% url 'users:profil' v.version.object.id %}">{{ v.version.object.get_username }}</a>
|
||||||
{% if v.comment != '' %}
|
{% if v.comment != '' %}
|
||||||
(<i>{{ v.comment }}</i>)
|
(<i>{{ v.comment }}</i>)
|
||||||
|
@ -97,7 +101,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
Annuler
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -106,17 +110,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ v.datetime }}</td>
|
<td>{{ v.datetime }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ v.username }} a vendu {{ v.version.object.number }}x {{ v.version.object.name }} à
|
{% blocktrans with username=v.username number=v.version.object.number name=v.version.object.name %}{{ username }} has sold {{ number }}x {{ name }}{% endblocktrans %}
|
||||||
<a href="{% url 'users:profil' v.version.object.facture.user_id %}">{{ v.version.object.facture.user.get_username }}</a>
|
{% with invoice=v.version.object.facture %}
|
||||||
|
{% if invoice|is_facture %}
|
||||||
|
{% trans " to" %}
|
||||||
|
<a href="{% url 'users:profil' v.version.object.facture.facture.user_id %}">{{ v.version.object.facture.facture.user.get_username }}</a>
|
||||||
{% if v.version.object.iscotisation %}
|
{% if v.version.object.iscotisation %}
|
||||||
(<i>+{{ v.version.object.duration }} mois</i>)
|
(<i>{% blocktrans with duration=v.version.object.duration %}+{{ duration }} months{% endblocktrans %}</i>)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
</td>
|
</td>
|
||||||
{% can_edit_history %}
|
{% can_edit_history %}
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
Annuler
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -125,7 +134,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ v.datetime }}</td>
|
<td>{{ v.datetime }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ v.username }} a modifié une interface de
|
{% blocktrans with username=v.username %}{{ username }} has edited an interface of{% endblocktrans %}
|
||||||
<a href="{% url 'users:profil' v.version.object.machine.user_id %}">{{ v.version.object.machine.user.get_username }}</a>
|
<a href="{% url 'users:profil' v.version.object.machine.user_id %}">{{ v.version.object.machine.user.get_username }}</a>
|
||||||
{% if v.comment != '' %}
|
{% if v.comment != '' %}
|
||||||
(<i>{{ v.comment }}</i>)
|
(<i>{{ v.comment }}</i>)
|
||||||
|
@ -135,7 +144,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>
|
<td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'logs:revert-action' v.rev_id %}">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
Annuler
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -147,3 +156,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% if versions_list.paginator %}
|
{% if versions_list.paginator %}
|
||||||
{% include "pagination.html" with list=versions_list %}
|
{% include "pagination.html" with list=versions_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Supression d'action{% endblock %}
|
{% block title %}{% trans "Deletion of actions" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h4>Attention, voulez-vous vraiment annuler cette action {{ objet_name }} ( {{ objet }} ) ?</h4>
|
<h4>{% blocktrans %}Warning: are you sure you want to delete this action {{ objet_name }} ( {{ objet }} )?{% endblocktrans %}</h4>
|
||||||
{% bootstrap_button "Confirmer" button_type="submit" icon="trash" %}
|
{% trans "Confirm" as tr_confirm %}
|
||||||
|
{% bootstrap_button tr_confirm button_type="submit" icon="trash" %}
|
||||||
</form>
|
</form>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Statistiques{% endblock %}
|
{% block title %}{%trans "Statistics" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Actions effectuées</h2>
|
<h2>{% trans "Actions performed" %}</h2>
|
||||||
{% include "logs/aff_summary.html" with versions_list=versions_list %}
|
{% include "logs/aff_summary.html" with versions_list=versions_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,32 +24,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block sidebar %}
|
{% block sidebar %}
|
||||||
{% can_view_app logs %}
|
{% can_view_app logs %}
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "logs:index" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "logs:index" %}">
|
||||||
<i class="fa fa-clipboard-list"></i>
|
<i class="fa fa-clipboard-list"></i>
|
||||||
Résumé
|
{% trans "Summary" %}
|
||||||
</a>
|
</a>
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-logs" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-logs" %}">
|
||||||
<i class="fa fa-calendar-alt"></i>
|
<i class="fa fa-calendar-alt"></i>
|
||||||
Évènements
|
{% trans "Events" %}
|
||||||
</a>
|
</a>
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-general" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-general" %}">
|
||||||
<i class="fa fa-chart-area"></i>
|
<i class="fa fa-chart-area"></i>
|
||||||
Général
|
{% trans "General" %}
|
||||||
</a>
|
</a>
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-models" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-models" %}">
|
||||||
<i class="fa fa-database"></i>
|
<i class="fa fa-database"></i>
|
||||||
Base de données
|
{% trans "Database" %}
|
||||||
</a>
|
</a>
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-actions" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-actions" %}">
|
||||||
<i class="fa fa-plug"></i>
|
<i class="fa fa-plug"></i>
|
||||||
Actions de cablage
|
{% trans "Wiring actions" %}
|
||||||
</a>
|
</a>
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-users" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-users" %}">
|
||||||
<i class="fa fa-users"></i>
|
<i class="fa fa-users"></i>
|
||||||
Utilisateurs
|
{% trans "Users" %}
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Statistiques générales{% endblock %}
|
{% block title %}{% trans "Statistics" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Statistiques générales</h2>
|
<h2>{% trans "General statistics" %}</h2>
|
||||||
{% include "logs/aff_stats_general.html" with stats_list=stats_list %}
|
{% include "logs/aff_stats_general.html" with stats_list=stats_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Statistiques{% endblock %}
|
{% block title %}{% trans "Statistics" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Actions effectuées</h2>
|
<h2>{% trans "Actions performed" %}</h2>
|
||||||
{% include "logs/aff_stats_logs.html" with revisions_list=revisions_list %}
|
{% include "logs/aff_stats_logs.html" with revisions_list=revisions_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Statistiques des objets base de données{% endblock %}
|
{% block title %}{% trans "Statistics" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Statistiques bdd</h2>
|
<h2>{% trans "Database statistics" %}</h2>
|
||||||
{% include "logs/aff_stats_models.html" with stats_list=stats_list %}
|
{% include "logs/aff_stats_models.html" with stats_list=stats_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Statistiques par utilisateur{% endblock %}
|
{% block title %}{% trans "Statistics" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Statistiques par utilisateur</h2>
|
<h2>{% trans "Statistics about users" %}</h2>
|
||||||
{% include "logs/aff_stats_users.html" with stats_list=stats_list %}
|
{% include "logs/aff_stats_users.html" with stats_list=stats_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 021}10-1301 USA.
|
||||||
"""logs.templatetags.logs_extra
|
"""logs.templatetags.logs_extra
|
||||||
A templatetag to get the class name for a given object
|
A templatetag to get the class name for a given object
|
||||||
"""
|
"""
|
||||||
|
@ -34,6 +34,10 @@ def classname(obj):
|
||||||
""" Returns the object class name """
|
""" Returns the object class name """
|
||||||
return obj.__class__.__name__
|
return obj.__class__.__name__
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def is_facture(baseinvoice):
|
||||||
|
"""Returns True if a baseinvoice has a `Facture` child."""
|
||||||
|
return hasattr(baseinvoice, 'facture')
|
||||||
|
|
||||||
@register.inclusion_tag('buttons/history.html')
|
@register.inclusion_tag('buttons/history.html')
|
||||||
def history_button(instance, text=False, html_class=True):
|
def history_button(instance, text=False, html_class=True):
|
||||||
|
|
165
logs/views.py
165
logs/views.py
|
@ -188,10 +188,10 @@ def revert_action(request, revision_id):
|
||||||
try:
|
try:
|
||||||
revision = Revision.objects.get(id=revision_id)
|
revision = Revision.objects.get(id=revision_id)
|
||||||
except Revision.DoesNotExist:
|
except Revision.DoesNotExist:
|
||||||
messages.error(request, u"Revision inexistante")
|
messages.error(request, _("Nonexistent revision."))
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
revision.revert()
|
revision.revert()
|
||||||
messages.success(request, "L'action a été supprimée")
|
messages.success(request, _("The action was deleted."))
|
||||||
return redirect(reverse('logs:index'))
|
return redirect(reverse('logs:index'))
|
||||||
return form({
|
return form({
|
||||||
'objet': revision,
|
'objet': revision,
|
||||||
|
@ -224,14 +224,14 @@ def stats_general(request):
|
||||||
stats = [
|
stats = [
|
||||||
[ # First set of data (about users)
|
[ # First set of data (about users)
|
||||||
[ # Headers
|
[ # Headers
|
||||||
"Categorie",
|
_("Category"),
|
||||||
"Nombre d'utilisateurs (total club et adhérents)",
|
_("Number of users (members and clubs)"),
|
||||||
"Nombre d'adhérents",
|
_("Number of members"),
|
||||||
"Nombre de clubs"
|
_("Number of clubs")
|
||||||
],
|
],
|
||||||
{ # Data
|
{ # Data
|
||||||
'active_users': [
|
'active_users': [
|
||||||
"Users actifs",
|
_("Activated users"),
|
||||||
User.objects.filter(state=User.STATE_ACTIVE).count(),
|
User.objects.filter(state=User.STATE_ACTIVE).count(),
|
||||||
(Adherent.objects
|
(Adherent.objects
|
||||||
.filter(state=Adherent.STATE_ACTIVE)
|
.filter(state=Adherent.STATE_ACTIVE)
|
||||||
|
@ -239,7 +239,7 @@ def stats_general(request):
|
||||||
Club.objects.filter(state=Club.STATE_ACTIVE).count()
|
Club.objects.filter(state=Club.STATE_ACTIVE).count()
|
||||||
],
|
],
|
||||||
'inactive_users': [
|
'inactive_users': [
|
||||||
"Users désactivés",
|
_("Disabled users"),
|
||||||
User.objects.filter(state=User.STATE_DISABLED).count(),
|
User.objects.filter(state=User.STATE_DISABLED).count(),
|
||||||
(Adherent.objects
|
(Adherent.objects
|
||||||
.filter(state=Adherent.STATE_DISABLED)
|
.filter(state=Adherent.STATE_DISABLED)
|
||||||
|
@ -247,7 +247,7 @@ def stats_general(request):
|
||||||
Club.objects.filter(state=Club.STATE_DISABLED).count()
|
Club.objects.filter(state=Club.STATE_DISABLED).count()
|
||||||
],
|
],
|
||||||
'archive_users': [
|
'archive_users': [
|
||||||
"Users archivés",
|
_("Archived users"),
|
||||||
User.objects.filter(state=User.STATE_ARCHIVE).count(),
|
User.objects.filter(state=User.STATE_ARCHIVE).count(),
|
||||||
(Adherent.objects
|
(Adherent.objects
|
||||||
.filter(state=Adherent.STATE_ARCHIVE)
|
.filter(state=Adherent.STATE_ARCHIVE)
|
||||||
|
@ -255,31 +255,31 @@ def stats_general(request):
|
||||||
Club.objects.filter(state=Club.STATE_ARCHIVE).count()
|
Club.objects.filter(state=Club.STATE_ARCHIVE).count()
|
||||||
],
|
],
|
||||||
'adherent_users': [
|
'adherent_users': [
|
||||||
"Cotisant à l'association",
|
_("Contributing members"),
|
||||||
_all_adherent.count(),
|
_all_adherent.count(),
|
||||||
_all_adherent.exclude(adherent__isnull=True).count(),
|
_all_adherent.exclude(adherent__isnull=True).count(),
|
||||||
_all_adherent.exclude(club__isnull=True).count()
|
_all_adherent.exclude(club__isnull=True).count()
|
||||||
],
|
],
|
||||||
'connexion_users': [
|
'connexion_users': [
|
||||||
"Utilisateurs bénéficiant d'une connexion",
|
_("Users benefiting from a connection"),
|
||||||
_all_has_access.count(),
|
_all_has_access.count(),
|
||||||
_all_has_access.exclude(adherent__isnull=True).count(),
|
_all_has_access.exclude(adherent__isnull=True).count(),
|
||||||
_all_has_access.exclude(club__isnull=True).count()
|
_all_has_access.exclude(club__isnull=True).count()
|
||||||
],
|
],
|
||||||
'ban_users': [
|
'ban_users': [
|
||||||
"Utilisateurs bannis",
|
_("Banned users"),
|
||||||
_all_baned.count(),
|
_all_baned.count(),
|
||||||
_all_baned.exclude(adherent__isnull=True).count(),
|
_all_baned.exclude(adherent__isnull=True).count(),
|
||||||
_all_baned.exclude(club__isnull=True).count()
|
_all_baned.exclude(club__isnull=True).count()
|
||||||
],
|
],
|
||||||
'whitelisted_user': [
|
'whitelisted_user': [
|
||||||
"Utilisateurs bénéficiant d'une connexion gracieuse",
|
_("Users benefiting from a free connection"),
|
||||||
_all_whitelisted.count(),
|
_all_whitelisted.count(),
|
||||||
_all_whitelisted.exclude(adherent__isnull=True).count(),
|
_all_whitelisted.exclude(adherent__isnull=True).count(),
|
||||||
_all_whitelisted.exclude(club__isnull=True).count()
|
_all_whitelisted.exclude(club__isnull=True).count()
|
||||||
],
|
],
|
||||||
'actives_interfaces': [
|
'actives_interfaces': [
|
||||||
"Interfaces actives (ayant accès au reseau)",
|
_("Active interfaces (with access to the network)"),
|
||||||
_all_active_interfaces_count.count(),
|
_all_active_interfaces_count.count(),
|
||||||
(_all_active_interfaces_count
|
(_all_active_interfaces_count
|
||||||
.exclude(machine__user__adherent__isnull=True)
|
.exclude(machine__user__adherent__isnull=True)
|
||||||
|
@ -289,7 +289,7 @@ def stats_general(request):
|
||||||
.count())
|
.count())
|
||||||
],
|
],
|
||||||
'actives_assigned_interfaces': [
|
'actives_assigned_interfaces': [
|
||||||
"Interfaces actives et assignées ipv4",
|
_("Active interfaces assigned IPv4"),
|
||||||
_all_active_assigned_interfaces_count.count(),
|
_all_active_assigned_interfaces_count.count(),
|
||||||
(_all_active_assigned_interfaces_count
|
(_all_active_assigned_interfaces_count
|
||||||
.exclude(machine__user__adherent__isnull=True)
|
.exclude(machine__user__adherent__isnull=True)
|
||||||
|
@ -302,12 +302,12 @@ def stats_general(request):
|
||||||
],
|
],
|
||||||
[ # Second set of data (about ip adresses)
|
[ # Second set of data (about ip adresses)
|
||||||
[ # Headers
|
[ # Headers
|
||||||
"Range d'ip",
|
_("IP range"),
|
||||||
"Vlan",
|
_("VLAN"),
|
||||||
"Nombre d'ip totales",
|
_("Total number of IP addresses"),
|
||||||
"Ip assignées",
|
_("Number of assigned IP addresses"),
|
||||||
"Ip assignées à une machine active",
|
_("Number of IP address assigned to an activated machine"),
|
||||||
"Ip non assignées"
|
_("Number of nonassigned IP addresses")
|
||||||
],
|
],
|
||||||
ip_dict # Data already prepared
|
ip_dict # Data already prepared
|
||||||
]
|
]
|
||||||
|
@ -322,79 +322,87 @@ def stats_models(request):
|
||||||
nombre d'users, d'écoles, de droits, de bannissements,
|
nombre d'users, d'écoles, de droits, de bannissements,
|
||||||
de factures, de ventes, de banque, de machines, etc"""
|
de factures, de ventes, de banque, de machines, etc"""
|
||||||
stats = {
|
stats = {
|
||||||
'Users': {
|
_("Users"): {
|
||||||
'users': [User.PRETTY_NAME, User.objects.count()],
|
'users': [User._meta.verbose_name, User.objects.count()],
|
||||||
'adherents': [Adherent.PRETTY_NAME, Adherent.objects.count()],
|
'adherents': [Adherent._meta.verbose_name, Adherent.objects.count()],
|
||||||
'clubs': [Club.PRETTY_NAME, Club.objects.count()],
|
'clubs': [Club._meta.verbose_name, Club.objects.count()],
|
||||||
'serviceuser': [ServiceUser.PRETTY_NAME,
|
'serviceuser': [ServiceUser._meta.verbose_name,
|
||||||
ServiceUser.objects.count()],
|
ServiceUser.objects.count()],
|
||||||
'school': [School.PRETTY_NAME, School.objects.count()],
|
'school': [School._meta.verbose_name, School.objects.count()],
|
||||||
'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()],
|
'listright': [ListRight._meta.verbose_name, ListRight.objects.count()],
|
||||||
'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()],
|
'listshell': [ListShell._meta.verbose_name, ListShell.objects.count()],
|
||||||
'ban': [Ban.PRETTY_NAME, Ban.objects.count()],
|
'ban': [Ban._meta.verbose_name, Ban.objects.count()],
|
||||||
'whitelist': [Whitelist.PRETTY_NAME, Whitelist.objects.count()]
|
'whitelist': [Whitelist._meta.verbose_name, Whitelist.objects.count()]
|
||||||
},
|
},
|
||||||
'Cotisations': {
|
_("Subscriptions"): {
|
||||||
'factures': [
|
'factures': [
|
||||||
Facture._meta.verbose_name.title(),
|
Facture._meta.verbose_name,
|
||||||
Facture.objects.count()
|
Facture.objects.count()
|
||||||
],
|
],
|
||||||
'vente': [
|
'vente': [
|
||||||
Vente._meta.verbose_name.title(),
|
Vente._meta.verbose_name,
|
||||||
Vente.objects.count()
|
Vente.objects.count()
|
||||||
],
|
],
|
||||||
'cotisation': [
|
'cotisation': [
|
||||||
Cotisation._meta.verbose_name.title(),
|
Cotisation._meta.verbose_name,
|
||||||
Cotisation.objects.count()
|
Cotisation.objects.count()
|
||||||
],
|
],
|
||||||
'article': [
|
'article': [
|
||||||
Article._meta.verbose_name.title(),
|
Article._meta.verbose_name,
|
||||||
Article.objects.count()
|
Article.objects.count()
|
||||||
],
|
],
|
||||||
'banque': [
|
'banque': [
|
||||||
Banque._meta.verbose_name.title(),
|
Banque._meta.verbose_name,
|
||||||
Banque.objects.count()
|
Banque.objects.count()
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'Machines': {
|
_("Machines"): {
|
||||||
'machine': [Machine.PRETTY_NAME, Machine.objects.count()],
|
'machine': [Machine._meta.verbose_name,
|
||||||
'typemachine': [MachineType.PRETTY_NAME,
|
Machine.objects.count()],
|
||||||
|
'typemachine': [MachineType._meta.verbose_name,
|
||||||
MachineType.objects.count()],
|
MachineType.objects.count()],
|
||||||
'typeip': [IpType.PRETTY_NAME, IpType.objects.count()],
|
'typeip': [IpType._meta.verbose_name,
|
||||||
'extension': [Extension.PRETTY_NAME, Extension.objects.count()],
|
IpType.objects.count()],
|
||||||
'interface': [Interface.PRETTY_NAME, Interface.objects.count()],
|
'extension': [Extension._meta.verbose_name,
|
||||||
'alias': [Domain.PRETTY_NAME,
|
Extension.objects.count()],
|
||||||
|
'interface': [Interface._meta.verbose_name,
|
||||||
|
Interface.objects.count()],
|
||||||
|
'alias': [Domain._meta.verbose_name,
|
||||||
Domain.objects.exclude(cname=None).count()],
|
Domain.objects.exclude(cname=None).count()],
|
||||||
'iplist': [IpList.PRETTY_NAME, IpList.objects.count()],
|
'iplist': [IpList._meta.verbose_name,
|
||||||
'service': [Service.PRETTY_NAME, Service.objects.count()],
|
IpList.objects.count()],
|
||||||
|
'service': [Service._meta.verbose_name,
|
||||||
|
Service.objects.count()],
|
||||||
'ouvertureportlist': [
|
'ouvertureportlist': [
|
||||||
OuverturePortList.PRETTY_NAME,
|
OuverturePortList._meta.verbose_name,
|
||||||
OuverturePortList.objects.count()
|
OuverturePortList.objects.count()
|
||||||
],
|
],
|
||||||
'vlan': [Vlan.PRETTY_NAME, Vlan.objects.count()],
|
'vlan': [Vlan._meta.verbose_name, Vlan.objects.count()],
|
||||||
'SOA': [SOA.PRETTY_NAME, SOA.objects.count()],
|
'SOA': [SOA._meta.verbose_name, SOA.objects.count()],
|
||||||
'Mx': [Mx.PRETTY_NAME, Mx.objects.count()],
|
'Mx': [Mx._meta.verbose_name, Mx.objects.count()],
|
||||||
'Ns': [Ns.PRETTY_NAME, Ns.objects.count()],
|
'Ns': [Ns._meta.verbose_name, Ns.objects.count()],
|
||||||
'nas': [Nas.PRETTY_NAME, Nas.objects.count()],
|
'nas': [Nas._meta.verbose_name, Nas.objects.count()],
|
||||||
},
|
},
|
||||||
'Topologie': {
|
_("Topology"): {
|
||||||
'switch': [Switch.PRETTY_NAME, Switch.objects.count()],
|
'switch': [Switch._meta.verbose_name,
|
||||||
'bornes': [AccessPoint.PRETTY_NAME, AccessPoint.objects.count()],
|
Switch.objects.count()],
|
||||||
'port': [Port.PRETTY_NAME, Port.objects.count()],
|
'bornes': [AccessPoint._meta.verbose_name,
|
||||||
'chambre': [Room.PRETTY_NAME, Room.objects.count()],
|
AccessPoint.objects.count()],
|
||||||
'stack': [Stack.PRETTY_NAME, Stack.objects.count()],
|
'port': [Port._meta.verbose_name, Port.objects.count()],
|
||||||
|
'chambre': [Room._meta.verbose_name, Room.objects.count()],
|
||||||
|
'stack': [Stack._meta.verbose_name, Stack.objects.count()],
|
||||||
'modelswitch': [
|
'modelswitch': [
|
||||||
ModelSwitch.PRETTY_NAME,
|
ModelSwitch._meta.verbose_name,
|
||||||
ModelSwitch.objects.count()
|
ModelSwitch.objects.count()
|
||||||
],
|
],
|
||||||
'constructorswitch': [
|
'constructorswitch': [
|
||||||
ConstructorSwitch.PRETTY_NAME,
|
ConstructorSwitch._meta.verbose_name,
|
||||||
ConstructorSwitch.objects.count()
|
ConstructorSwitch.objects.count()
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'Actions effectuées sur la base':
|
_("Actions performed"):
|
||||||
{
|
{
|
||||||
'revision': ["Nombre d'actions", Revision.objects.count()],
|
'revision': [_("Number of actions"), Revision.objects.count()],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return render(request, 'logs/stats_models.html', {'stats_list': stats})
|
return render(request, 'logs/stats_models.html', {'stats_list': stats})
|
||||||
|
@ -408,35 +416,35 @@ def stats_users(request):
|
||||||
de moyens de paiements par user, de banque par user,
|
de moyens de paiements par user, de banque par user,
|
||||||
de bannissement par user, etc"""
|
de bannissement par user, etc"""
|
||||||
stats = {
|
stats = {
|
||||||
'Utilisateur': {
|
_("User"): {
|
||||||
'Machines': User.objects.annotate(
|
_("Machines"): User.objects.annotate(
|
||||||
num=Count('machine')
|
num=Count('machine')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
'Facture': User.objects.annotate(
|
_("Invoice"): User.objects.annotate(
|
||||||
num=Count('facture')
|
num=Count('facture')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
'Bannissement': User.objects.annotate(
|
_("Ban"): User.objects.annotate(
|
||||||
num=Count('ban')
|
num=Count('ban')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
'Accès gracieux': User.objects.annotate(
|
_("Whitelist"): User.objects.annotate(
|
||||||
num=Count('whitelist')
|
num=Count('whitelist')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
'Droits': User.objects.annotate(
|
_("Rights"): User.objects.annotate(
|
||||||
num=Count('groups')
|
num=Count('groups')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
},
|
},
|
||||||
'Etablissement': {
|
_("School"): {
|
||||||
'Utilisateur': School.objects.annotate(
|
_("User"): School.objects.annotate(
|
||||||
num=Count('user')
|
num=Count('user')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
},
|
},
|
||||||
'Moyen de paiement': {
|
_("Payment method"): {
|
||||||
'Utilisateur': Paiement.objects.annotate(
|
_("User"): Paiement.objects.annotate(
|
||||||
num=Count('facture')
|
num=Count('facture')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
},
|
},
|
||||||
'Banque': {
|
_("Bank"): {
|
||||||
'Utilisateur': Banque.objects.annotate(
|
_("User"): Banque.objects.annotate(
|
||||||
num=Count('facture')
|
num=Count('facture')
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
},
|
},
|
||||||
|
@ -451,8 +459,8 @@ def stats_actions(request):
|
||||||
utilisateurs.
|
utilisateurs.
|
||||||
Affiche le nombre de modifications aggrégées par utilisateurs"""
|
Affiche le nombre de modifications aggrégées par utilisateurs"""
|
||||||
stats = {
|
stats = {
|
||||||
'Utilisateur': {
|
_("User"): {
|
||||||
'Action': User.objects.annotate(
|
_("Action"): User.objects.annotate(
|
||||||
num=Count('revision')
|
num=Count('revision')
|
||||||
).order_by('-num')[:40],
|
).order_by('-num')[:40],
|
||||||
},
|
},
|
||||||
|
@ -489,14 +497,14 @@ def history(request, application, object_name, object_id):
|
||||||
try:
|
try:
|
||||||
instance = model.get_instance(**kwargs)
|
instance = model.get_instance(**kwargs)
|
||||||
except model.DoesNotExist:
|
except model.DoesNotExist:
|
||||||
messages.error(request, _("No entry found."))
|
messages.error(request, _("Nonexistent entry."))
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid': str(request.user.id)}
|
kwargs={'userid': str(request.user.id)}
|
||||||
))
|
))
|
||||||
can, msg = instance.can_view(request.user)
|
can, msg = instance.can_view(request.user)
|
||||||
if not can:
|
if not can:
|
||||||
messages.error(request, msg or _("You cannot acces to this menu"))
|
messages.error(request, msg or _("You don't have the right to access this menu."))
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid': str(request.user.id)}
|
kwargs={'userid': str(request.user.id)}
|
||||||
|
@ -513,3 +521,4 @@ def history(request, application, object_name, object_id):
|
||||||
're2o/history.html',
|
're2o/history.html',
|
||||||
{'reversions': reversions, 'object': instance}
|
{'reversions': reversions, 'object': instance}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
|
@ -38,4 +39,6 @@ def can_view(user):
|
||||||
viewing is granted and msg is a message (can be None).
|
viewing is granted and msg is a message (can be None).
|
||||||
"""
|
"""
|
||||||
can = user.has_module_perms('machines')
|
can = user.has_module_perms('machines')
|
||||||
return can, None if can else "Vous ne pouvez pas voir cette application."
|
return can, None if can else _("You don't have the right to view this"
|
||||||
|
" application.")
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ from .models import (
|
||||||
SshFp,
|
SshFp,
|
||||||
Nas,
|
Nas,
|
||||||
Service,
|
Service,
|
||||||
|
Role,
|
||||||
OuverturePort,
|
OuverturePort,
|
||||||
Ipv6List,
|
Ipv6List,
|
||||||
OuverturePortList,
|
OuverturePortList,
|
||||||
|
@ -146,6 +147,11 @@ class ServiceAdmin(VersionAdmin):
|
||||||
""" Admin view of a ServiceAdmin object """
|
""" Admin view of a ServiceAdmin object """
|
||||||
list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
|
list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
|
||||||
|
|
||||||
|
class RoleAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a RoleAdmin object """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Machine, MachineAdmin)
|
admin.site.register(Machine, MachineAdmin)
|
||||||
admin.site.register(MachineType, MachineTypeAdmin)
|
admin.site.register(MachineType, MachineTypeAdmin)
|
||||||
|
@ -162,6 +168,7 @@ admin.site.register(IpList, IpListAdmin)
|
||||||
admin.site.register(Interface, InterfaceAdmin)
|
admin.site.register(Interface, InterfaceAdmin)
|
||||||
admin.site.register(Domain, DomainAdmin)
|
admin.site.register(Domain, DomainAdmin)
|
||||||
admin.site.register(Service, ServiceAdmin)
|
admin.site.register(Service, ServiceAdmin)
|
||||||
|
admin.site.register(Role, RoleAdmin)
|
||||||
admin.site.register(Vlan, VlanAdmin)
|
admin.site.register(Vlan, VlanAdmin)
|
||||||
admin.site.register(Ipv6List, Ipv6ListAdmin)
|
admin.site.register(Ipv6List, Ipv6ListAdmin)
|
||||||
admin.site.register(Nas, NasAdmin)
|
admin.site.register(Nas, NasAdmin)
|
||||||
|
|
|
@ -37,6 +37,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.forms import ModelForm, Form
|
from django.forms import ModelForm, Form
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
|
@ -53,6 +54,7 @@ from .models import (
|
||||||
Txt,
|
Txt,
|
||||||
DName,
|
DName,
|
||||||
Ns,
|
Ns,
|
||||||
|
Role,
|
||||||
Service,
|
Service,
|
||||||
Vlan,
|
Vlan,
|
||||||
Srv,
|
Srv,
|
||||||
|
@ -73,7 +75,7 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['name'].label = 'Nom de la machine'
|
self.fields['name'].label = _("Machine name")
|
||||||
|
|
||||||
|
|
||||||
class NewMachineForm(EditMachineForm):
|
class NewMachineForm(EditMachineForm):
|
||||||
|
@ -92,12 +94,11 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
user = kwargs.get('user')
|
user = kwargs.get('user')
|
||||||
super(EditInterfaceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditInterfaceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['mac_address'].label = 'Adresse mac'
|
self.fields['mac_address'].label = _("MAC address")
|
||||||
self.fields['type'].label = 'Type de machine'
|
self.fields['type'].label = _("Machine type")
|
||||||
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
self.fields['type'].empty_label = _("Select a machine type")
|
||||||
if "ipv4" in self.fields:
|
if "ipv4" in self.fields:
|
||||||
self.fields['ipv4'].empty_label = ("Assignation automatique de "
|
self.fields['ipv4'].empty_label = _("Automatic IPv4 assignment")
|
||||||
"l'ipv4")
|
|
||||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||||
interface__isnull=True
|
interface__isnull=True
|
||||||
)
|
)
|
||||||
|
@ -168,7 +169,7 @@ class DelAliasForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs objets alias"""
|
"""Suppression d'un ou plusieurs objets alias"""
|
||||||
alias = forms.ModelMultipleChoiceField(
|
alias = forms.ModelMultipleChoiceField(
|
||||||
queryset=Domain.objects.all(),
|
queryset=Domain.objects.all(),
|
||||||
label="Alias actuels",
|
label=_("Current aliases"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -189,15 +190,15 @@ class MachineTypeForm(FormRevMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(MachineTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(MachineTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['type'].label = 'Type de machine à ajouter'
|
self.fields['type'].label = _("Machine type to add")
|
||||||
self.fields['ip_type'].label = "Type d'ip relié"
|
self.fields['ip_type'].label = _("Related IP type")
|
||||||
|
|
||||||
|
|
||||||
class DelMachineTypeForm(FormRevMixin, Form):
|
class DelMachineTypeForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs machinetype"""
|
"""Suppression d'un ou plusieurs machinetype"""
|
||||||
machinetypes = forms.ModelMultipleChoiceField(
|
machinetypes = forms.ModelMultipleChoiceField(
|
||||||
queryset=MachineType.objects.none(),
|
queryset=MachineType.objects.none(),
|
||||||
label="Types de machines actuelles",
|
label=_("Current machine types"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -215,20 +216,21 @@ class IpTypeForm(FormRevMixin, ModelForm):
|
||||||
stop après creation"""
|
stop après creation"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IpType
|
model = IpType
|
||||||
fields = ['type', 'extension', 'need_infra', 'domaine_ip_start',
|
fields = '__all__'
|
||||||
'domaine_ip_stop', 'prefix_v6', 'vlan', 'ouverture_ports']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['type'].label = 'Type ip à ajouter'
|
self.fields['type'].label = _("IP type to add")
|
||||||
|
|
||||||
|
|
||||||
class EditIpTypeForm(IpTypeForm):
|
class EditIpTypeForm(IpTypeForm):
|
||||||
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait
|
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait
|
||||||
synchroniser les objets iplist"""
|
synchroniser les objets iplist"""
|
||||||
class Meta(IpTypeForm.Meta):
|
class Meta(IpTypeForm.Meta):
|
||||||
fields = ['extension', 'type', 'need_infra', 'prefix_v6', 'vlan',
|
fields = ['extension', 'type', 'need_infra', 'domaine_ip_network', 'domaine_ip_netmask',
|
||||||
|
'prefix_v6', 'prefix_v6_length',
|
||||||
|
'vlan', 'reverse_v4', 'reverse_v6',
|
||||||
'ouverture_ports']
|
'ouverture_ports']
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,7 +238,7 @@ class DelIpTypeForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs iptype"""
|
"""Suppression d'un ou plusieurs iptype"""
|
||||||
iptypes = forms.ModelMultipleChoiceField(
|
iptypes = forms.ModelMultipleChoiceField(
|
||||||
queryset=IpType.objects.none(),
|
queryset=IpType.objects.none(),
|
||||||
label="Types d'ip actuelles",
|
label=_("Current IP types"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -258,17 +260,17 @@ class ExtensionForm(FormRevMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['name'].label = 'Extension à ajouter'
|
self.fields['name'].label = _("Extension to add")
|
||||||
self.fields['origin'].label = 'Enregistrement A origin'
|
self.fields['origin'].label = _("A record origin")
|
||||||
self.fields['origin_v6'].label = 'Enregistrement AAAA origin'
|
self.fields['origin_v6'].label = _("AAAA record origin")
|
||||||
self.fields['soa'].label = 'En-tête SOA à utiliser'
|
self.fields['soa'].label = _("SOA record to use")
|
||||||
|
|
||||||
|
|
||||||
class DelExtensionForm(FormRevMixin, Form):
|
class DelExtensionForm(FormRevMixin, Form):
|
||||||
"""Suppression d'une ou plusieurs extensions"""
|
"""Suppression d'une ou plusieurs extensions"""
|
||||||
extensions = forms.ModelMultipleChoiceField(
|
extensions = forms.ModelMultipleChoiceField(
|
||||||
queryset=Extension.objects.none(),
|
queryset=Extension.objects.none(),
|
||||||
label="Extensions actuelles",
|
label=_("Current extensions"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -307,7 +309,7 @@ class DelSOAForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs SOA"""
|
"""Suppression d'un ou plusieurs SOA"""
|
||||||
soa = forms.ModelMultipleChoiceField(
|
soa = forms.ModelMultipleChoiceField(
|
||||||
queryset=SOA.objects.none(),
|
queryset=SOA.objects.none(),
|
||||||
label="SOA actuels",
|
label=_("Current SOA records"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -338,7 +340,7 @@ class DelMxForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs MX"""
|
"""Suppression d'un ou plusieurs MX"""
|
||||||
mx = forms.ModelMultipleChoiceField(
|
mx = forms.ModelMultipleChoiceField(
|
||||||
queryset=Mx.objects.none(),
|
queryset=Mx.objects.none(),
|
||||||
label="MX actuels",
|
label=_("Current MX records"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -371,7 +373,7 @@ class DelNsForm(FormRevMixin, Form):
|
||||||
"""Suppresion d'un ou plusieurs NS"""
|
"""Suppresion d'un ou plusieurs NS"""
|
||||||
ns = forms.ModelMultipleChoiceField(
|
ns = forms.ModelMultipleChoiceField(
|
||||||
queryset=Ns.objects.none(),
|
queryset=Ns.objects.none(),
|
||||||
label="Enregistrements NS actuels",
|
label=_("Current NS records"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -399,7 +401,7 @@ class DelTxtForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs TXT"""
|
"""Suppression d'un ou plusieurs TXT"""
|
||||||
txt = forms.ModelMultipleChoiceField(
|
txt = forms.ModelMultipleChoiceField(
|
||||||
queryset=Txt.objects.none(),
|
queryset=Txt.objects.none(),
|
||||||
label="Enregistrements Txt actuels",
|
label=_("Current TXT records"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -427,7 +429,7 @@ class DelDNameForm(FormRevMixin, Form):
|
||||||
"""Delete a set of DNAME entries"""
|
"""Delete a set of DNAME entries"""
|
||||||
dnames = forms.ModelMultipleChoiceField(
|
dnames = forms.ModelMultipleChoiceField(
|
||||||
queryset=Txt.objects.none(),
|
queryset=Txt.objects.none(),
|
||||||
label="Existing DNAME entries",
|
label=_("Current DNAME records"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -455,7 +457,7 @@ class DelSrvForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs Srv"""
|
"""Suppression d'un ou plusieurs Srv"""
|
||||||
srv = forms.ModelMultipleChoiceField(
|
srv = forms.ModelMultipleChoiceField(
|
||||||
queryset=Srv.objects.none(),
|
queryset=Srv.objects.none(),
|
||||||
label="Enregistrements Srv actuels",
|
label=_("Current SRV records"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -484,7 +486,7 @@ class DelNasForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs nas"""
|
"""Suppression d'un ou plusieurs nas"""
|
||||||
nas = forms.ModelMultipleChoiceField(
|
nas = forms.ModelMultipleChoiceField(
|
||||||
queryset=Nas.objects.none(),
|
queryset=Nas.objects.none(),
|
||||||
label="Enregistrements Nas actuels",
|
label=_("Current NAS devices"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -497,6 +499,38 @@ class DelNasForm(FormRevMixin, Form):
|
||||||
self.fields['nas'].queryset = Nas.objects.all()
|
self.fields['nas'].queryset = Nas.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class RoleForm(FormRevMixin, ModelForm):
|
||||||
|
"""Add and edit role."""
|
||||||
|
class Meta:
|
||||||
|
model = Role
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
|
super(RoleForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
|
self.fields['servers'].queryset = (Interface.objects.all()
|
||||||
|
.select_related(
|
||||||
|
'domain__extension'
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class DelRoleForm(FormRevMixin, Form):
|
||||||
|
"""Deletion of one or several roles."""
|
||||||
|
role = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=Role.objects.none(),
|
||||||
|
label=_("Current roles"),
|
||||||
|
widget=forms.CheckboxSelectMultiple
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
instances = kwargs.pop('instances', None)
|
||||||
|
super(DelRoleForm, self).__init__(*args, **kwargs)
|
||||||
|
if instances:
|
||||||
|
self.fields['role'].queryset = instances
|
||||||
|
else:
|
||||||
|
self.fields['role'].queryset = Role.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class ServiceForm(FormRevMixin, ModelForm):
|
class ServiceForm(FormRevMixin, ModelForm):
|
||||||
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
|
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -525,7 +559,7 @@ class DelServiceForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs service"""
|
"""Suppression d'un ou plusieurs service"""
|
||||||
service = forms.ModelMultipleChoiceField(
|
service = forms.ModelMultipleChoiceField(
|
||||||
queryset=Service.objects.none(),
|
queryset=Service.objects.none(),
|
||||||
label="Services actuels",
|
label=_("Current services"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -553,7 +587,7 @@ class DelVlanForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs vlans"""
|
"""Suppression d'un ou plusieurs vlans"""
|
||||||
vlan = forms.ModelMultipleChoiceField(
|
vlan = forms.ModelMultipleChoiceField(
|
||||||
queryset=Vlan.objects.none(),
|
queryset=Vlan.objects.none(),
|
||||||
label="Vlan actuels",
|
label=_("Current VLANs"),
|
||||||
widget=forms.CheckboxSelectMultiple
|
widget=forms.CheckboxSelectMultiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -611,3 +645,4 @@ class SshFpForm(FormRevMixin, ModelForm):
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
BIN
machines/locale/fr/LC_MESSAGES/django.mo
Normal file
BIN
machines/locale/fr/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
1748
machines/locale/fr/LC_MESSAGES/django.po
Normal file
1748
machines/locale/fr/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load diff
27
machines/migrations/0086_role.py
Normal file
27
machines/migrations/0086_role.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-06-23 14:07
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import re2o.mixins
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0085_sshfingerprint'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Role',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('role_type', models.CharField(max_length=255, unique=True)),
|
||||||
|
('servers', models.ManyToManyField(to='machines.Interface')),
|
||||||
|
('specific_role', models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursif-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'Radius server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gatewaw')], max_length=32, null=True))
|
||||||
|
],
|
||||||
|
options={'permissions': (('view_role', 'Can view a role.'),), 'verbose_name': 'Server role'},
|
||||||
|
bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model),
|
||||||
|
),
|
||||||
|
]
|
25
machines/migrations/0087_dnssec.py
Normal file
25
machines/migrations/0087_dnssec.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-06-25 15:06
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0086_role'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='dnssec_reverse_v4',
|
||||||
|
field=models.BooleanField(default=False, help_text='Activer DNSSEC sur le reverse DNS IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='dnssec_reverse_v6',
|
||||||
|
field=models.BooleanField(default=False, help_text='Activer DNSSEC sur le reverse DNS IPv6'),
|
||||||
|
),
|
||||||
|
]
|
21
machines/migrations/0088_iptype_prefix_v6_length.py
Normal file
21
machines/migrations/0088_iptype_prefix_v6_length.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-07-16 18:46
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0087_dnssec'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='prefix_v6_length',
|
||||||
|
field=models.IntegerField(default=64, validators=[django.core.validators.MaxValueValidator(128), django.core.validators.MinValueValidator(0)]),
|
||||||
|
),
|
||||||
|
]
|
21
machines/migrations/0089_auto_20180805_1148.py
Normal file
21
machines/migrations/0089_auto_20180805_1148.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-05 09:48
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import macaddress.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0088_iptype_prefix_v6_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='interface',
|
||||||
|
name='mac_address',
|
||||||
|
field=macaddress.fields.MACAddressField(integer=False, max_length=17),
|
||||||
|
),
|
||||||
|
]
|
20
machines/migrations/0090_auto_20180805_1459.py
Normal file
20
machines/migrations/0090_auto_20180805_1459.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-05 12:59
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0089_auto_20180805_1148'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ipv6list',
|
||||||
|
name='ipv6',
|
||||||
|
field=models.GenericIPAddressField(protocol='IPv6'),
|
||||||
|
),
|
||||||
|
]
|
26
machines/migrations/0091_auto_20180806_2310.py
Normal file
26
machines/migrations/0091_auto_20180806_2310.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-06 21:10
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0090_auto_20180805_1459'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='domaine_ip_netmask',
|
||||||
|
field=models.IntegerField(default=24, help_text='Netmask for the ipv4 range domain', validators=[django.core.validators.MaxValueValidator(31), django.core.validators.MinValueValidator(8)]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='domaine_ip_network',
|
||||||
|
field=models.GenericIPAddressField(blank=True, help_text='Network containing the ipv4 range domain ip start/stop. Optional', null=True, protocol='IPv4'),
|
||||||
|
),
|
||||||
|
]
|
25
machines/migrations/0092_auto_20180807_0926.py
Normal file
25
machines/migrations/0092_auto_20180807_0926.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-07 07:26
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0091_auto_20180806_2310'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='iptype',
|
||||||
|
old_name='dnssec_reverse_v4',
|
||||||
|
new_name='reverse_v4',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='iptype',
|
||||||
|
old_name='dnssec_reverse_v6',
|
||||||
|
new_name='reverse_v6',
|
||||||
|
),
|
||||||
|
]
|
26
machines/migrations/0093_auto_20180807_1115.py
Normal file
26
machines/migrations/0093_auto_20180807_1115.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-07 09:15
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0092_auto_20180807_0926'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='mx',
|
||||||
|
name='name',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='mx',
|
||||||
|
name='priority',
|
||||||
|
field=models.PositiveIntegerField(),
|
||||||
|
),
|
||||||
|
]
|
221
machines/migrations/0094_auto_20180815_1918.py
Normal file
221
machines/migrations/0094_auto_20180815_1918.py
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-08-15 17:18
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0093_auto_20180807_1115'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='dname',
|
||||||
|
options={'permissions': (('view_dname', 'Can view a DNAME record object'),), 'verbose_name': 'DNAME record', 'verbose_name_plural': 'DNAME records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='domain',
|
||||||
|
options={'permissions': (('view_domain', 'Can view a domain object'),), 'verbose_name': 'domain', 'verbose_name_plural': 'domains'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='extension',
|
||||||
|
options={'permissions': (('view_extension', 'Can view an extension object'), ('use_all_extension', 'Can use all extensions')), 'verbose_name': 'DNS extension', 'verbose_name_plural': 'DNS extensions'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='interface',
|
||||||
|
options={'permissions': (('view_interface', 'Can view an interface object'), ('change_interface_machine', 'Can change the owner of an interface')), 'verbose_name': 'interface', 'verbose_name_plural': 'interfaces'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='iplist',
|
||||||
|
options={'permissions': (('view_iplist', 'Can view an IPv4 addresses list object'),), 'verbose_name': 'IPv4 addresses list', 'verbose_name_plural': 'IPv4 addresses lists'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='iptype',
|
||||||
|
options={'permissions': (('view_iptype', 'Can view an IP type object'), ('use_all_iptype', 'Can use all IP types')), 'verbose_name': 'IP type', 'verbose_name_plural': 'IP types'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='ipv6list',
|
||||||
|
options={'permissions': (('view_ipv6list', 'Can view an IPv6 addresses list object'), ('change_ipv6list_slaac_ip', 'Can change the SLAAC value of an IPv6 addresses list')), 'verbose_name': 'IPv6 addresses list', 'verbose_name_plural': 'IPv6 addresses lists'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='machine',
|
||||||
|
options={'permissions': (('view_machine', 'Can view a machine object'), ('change_machine_user', 'Can change the user of a machine')), 'verbose_name': 'machine', 'verbose_name_plural': 'machines'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='machinetype',
|
||||||
|
options={'permissions': (('view_machinetype', 'Can view a machine type object'), ('use_all_machinetype', 'Can use all machine types')), 'verbose_name': 'machine type', 'verbose_name_plural': 'machine types'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='mx',
|
||||||
|
options={'permissions': (('view_mx', 'Can view an MX record object'),), 'verbose_name': 'MX record', 'verbose_name_plural': 'MX records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='nas',
|
||||||
|
options={'permissions': (('view_nas', 'Can view a NAS device object'),), 'verbose_name': 'NAS device', 'verbose_name_plural': 'NAS devices'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='ns',
|
||||||
|
options={'permissions': (('view_ns', 'Can view an NS record object'),), 'verbose_name': 'NS record', 'verbose_name_plural': 'NS records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='ouvertureport',
|
||||||
|
options={'verbose_name': 'ports openings'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='ouvertureportlist',
|
||||||
|
options={'permissions': (('view_ouvertureportlist', 'Can view a ports opening list object'),), 'verbose_name': 'ports opening list', 'verbose_name_plural': 'ports opening lists'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='role',
|
||||||
|
options={'permissions': (('view_role', 'Can view a role object'),), 'verbose_name': 'server role', 'verbose_name_plural': 'server roles'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='service',
|
||||||
|
options={'permissions': (('view_service', 'Can view a service object'),), 'verbose_name': 'service to generate (DHCP, DNS, ...)', 'verbose_name_plural': 'services to generate (DHCP, DNS, ...)'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='service_link',
|
||||||
|
options={'permissions': (('view_service_link', 'Can view a service server link object'),), 'verbose_name': 'link between service and server', 'verbose_name_plural': 'links between service and server'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='soa',
|
||||||
|
options={'permissions': (('view_soa', 'Can view an SOA record object'),), 'verbose_name': 'SOA record', 'verbose_name_plural': 'SOA records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='srv',
|
||||||
|
options={'permissions': (('view_srv', 'Can view an SRV record object'),), 'verbose_name': 'SRV record', 'verbose_name_plural': 'SRV records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='sshfp',
|
||||||
|
options={'permissions': (('view_sshfp', 'Can view an SSHFP record object'),), 'verbose_name': 'SSHFP record', 'verbose_name_plural': 'SSHFP records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='txt',
|
||||||
|
options={'permissions': (('view_txt', 'Can view a TXT record object'),), 'verbose_name': 'TXT record', 'verbose_name_plural': 'TXT records'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='vlan',
|
||||||
|
options={'permissions': (('view_vlan', 'Can view a VLAN object'),), 'verbose_name': 'VLAN', 'verbose_name_plural': 'VLANs'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='domain',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Mandatory and unique, must not contain dots.', max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='extension',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Zone name, must begin with a dot (.example.org)', max_length=255, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='extension',
|
||||||
|
name='origin',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='A record associated with the zone', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='extension',
|
||||||
|
name='origin_v6',
|
||||||
|
field=models.GenericIPAddressField(blank=True, help_text='AAAA record associated with the zone', null=True, protocol='IPv6'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='domaine_ip_netmask',
|
||||||
|
field=models.IntegerField(default=24, help_text="Netmask for the domain's IPv4 range", validators=[django.core.validators.MaxValueValidator(31), django.core.validators.MinValueValidator(8)]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='domaine_ip_network',
|
||||||
|
field=models.GenericIPAddressField(blank=True, help_text="Network containing the domain's IPv4 range (optional)", null=True, protocol='IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='reverse_v4',
|
||||||
|
field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='reverse_v6',
|
||||||
|
field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv6'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='machine',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(blank=True, help_text='Optional', max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ouvertureportlist',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Name of the ports configuration', max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='role',
|
||||||
|
name='specific_role',
|
||||||
|
field=models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursif-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'RADIUS server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gateway')], max_length=32, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='service',
|
||||||
|
name='min_time_regen',
|
||||||
|
field=models.DurationField(default=datetime.timedelta(0, 60), help_text='Minimal time before regeneration of the service.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='service',
|
||||||
|
name='regular_time_regen',
|
||||||
|
field=models.DurationField(default=datetime.timedelta(0, 3600), help_text='Maximal time before regeneration of the service.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='soa',
|
||||||
|
name='expire',
|
||||||
|
field=models.PositiveIntegerField(default=3600000, help_text='Seconds before the secondary DNS stop answering requests in case of primary DNS timeout'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='soa',
|
||||||
|
name='mail',
|
||||||
|
field=models.EmailField(help_text='Contact email address for the zone', max_length=254),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='soa',
|
||||||
|
name='refresh',
|
||||||
|
field=models.PositiveIntegerField(default=86400, help_text='Seconds before the secondary DNS have to ask the primary DNS serial to detect a modification'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='soa',
|
||||||
|
name='retry',
|
||||||
|
field=models.PositiveIntegerField(default=7200, help_text='Seconds before the secondary DNS ask the serial again in case of a primary DNS timeout'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='soa',
|
||||||
|
name='ttl',
|
||||||
|
field=models.PositiveIntegerField(default=172800, help_text='Time to Live'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='srv',
|
||||||
|
name='port',
|
||||||
|
field=models.PositiveIntegerField(help_text='TCP/UDP port', validators=[django.core.validators.MaxValueValidator(65535)]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='srv',
|
||||||
|
name='priority',
|
||||||
|
field=models.PositiveIntegerField(default=0, help_text='Priority of the target server (positive integer value, the lower it is, the more the server will be used if available)', validators=[django.core.validators.MaxValueValidator(65535)]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='srv',
|
||||||
|
name='target',
|
||||||
|
field=models.ForeignKey(help_text='Target server', on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='srv',
|
||||||
|
name='ttl',
|
||||||
|
field=models.PositiveIntegerField(default=172800, help_text='Time to Live'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='srv',
|
||||||
|
name='weight',
|
||||||
|
field=models.PositiveIntegerField(default=0, help_text='Relative weight for records with the same priority (integer value between 0 and 65535)', validators=[django.core.validators.MaxValueValidator(65535)]),
|
||||||
|
),
|
||||||
|
]
|
File diff suppressed because it is too large
Load diff
|
@ -23,12 +23,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Alias</th>
|
<th>{% trans "Aliases" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -22,12 +22,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Target zone</th>
|
<th>{% trans "Target zone" %}</th>
|
||||||
<th>Record</th>
|
<th>{% trans "Record" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -45,4 +46,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,17 +25,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
{% load design %}
|
{% load design %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Extension</th>
|
<th>{% trans "Extension" %}</th>
|
||||||
<th>Droit infra pour utiliser ?</th>
|
<th>{% trans "'infra' right required" %}</th>
|
||||||
<th>Enregistrement SOA</th>
|
<th>{% trans "SOA record" %}</th>
|
||||||
<th>Enregistrement A origin</th>
|
<th>{% trans "A record origin" %}</th>
|
||||||
{% if ipv6_enabled %}
|
{% if ipv6_enabled %}
|
||||||
<th>Enregistrement AAAA origin</th>
|
<th>{% trans "AAAA record origin" %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -59,3 +60,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -26,18 +26,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type d'ip</th>
|
<th>{% trans "IP type" %}</th>
|
||||||
<th>Extension</th>
|
<th>{% trans "Extension" %}</th>
|
||||||
<th>Nécessite l'autorisation infra</th>
|
<th>{% trans "'infra' right required" %}</th>
|
||||||
<th>Plage ipv4</th>
|
<th>{% trans "IPv4 range" %}</th>
|
||||||
<th>Préfixe v6</th>
|
<th>{% trans "v6 prefix" %}</th>
|
||||||
<th>Sur vlan</th>
|
<th>{% trans "DNSSEC reverse v4/v6" %}</th>
|
||||||
<th>Ouverture ports par défault</th>
|
<th>{% trans "On VLAN(s)" %}</th>
|
||||||
<th></th>
|
<th>{% trans "Default ports opening" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -46,8 +48,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ type.type }}</td>
|
<td>{{ type.type }}</td>
|
||||||
<td>{{ type.extension }}</td>
|
<td>{{ type.extension }}</td>
|
||||||
<td>{{ type.need_infra|tick }}</td>
|
<td>{{ type.need_infra|tick }}</td>
|
||||||
<td>{{ type.domaine_ip_start }}-{{ type.domaine_ip_stop }}</td>
|
<td>{{ type.domaine_ip_start }}-{{ type.domaine_ip_stop }}{% if type.ip_network %}<b><u> on </b></u>{{ type.ip_network }}{% endif %}</td>
|
||||||
<td>{{ type.prefix_v6 }}</td>
|
<td>{{ type.prefix_v6 }}/{{ type.prefix_v6_length }}</td>
|
||||||
|
<td>{{ type.reverse_v4|tick }}/{{ type.reverse_v6|tick }}</td>
|
||||||
<td>{{ type.vlan }}</td>
|
<td>{{ type.vlan }}</td>
|
||||||
<td>{{ type.ouverture_ports }}</td>
|
<td>{{ type.ouverture_ports }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
|
@ -60,3 +63,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Ipv6</th>
|
<th>{% trans "IPv6 addresses" %}</th>
|
||||||
<th>Slaac</th>
|
<th>{% trans "SLAAC" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
{% if machines_list.paginator %}
|
{% if machines_list.paginator %}
|
||||||
|
@ -39,23 +40,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<col width="144px">
|
<col width="144px">
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<th>{% include "buttons/sort.html" with prefix='machine' col='name' text='Nom DNS' %}</th>
|
{% trans "DNS name" as tr_dns_name %}
|
||||||
<th>Type</th>
|
<th>{% include "buttons/sort.html" with prefix='machine' col='name' text=tr_dns_name %}</th>
|
||||||
<th>MAC</th>
|
<th>{% trans "Type" %}</th>
|
||||||
<th>IP</th>
|
<th>{% trans "MAC address" %}</th>
|
||||||
<th>Actions</th>
|
<th>{% trans "IP address" %}</th>
|
||||||
|
<th>{% trans "Actions" %}</th>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for machine in machines_list %}
|
{% for machine in machines_list %}
|
||||||
<tr class="info">
|
<tr class="info">
|
||||||
<td colspan="4">
|
<td colspan="4">
|
||||||
<b>{{ machine.name|default:'<i>Pas de nom</i>' }}</b> <i class="fa-angle-right"></i>
|
{% trans "No name" as tr_no_name %}
|
||||||
<a href="{% url 'users:profil' userid=machine.user.id %}" title="Voir le profil">
|
{% trans "View the profile" as tr_view_the_profile %}
|
||||||
|
<b>{{ machine.name|default:tr_no_name }}</b> <i class="fa-angle-right"></i>
|
||||||
|
<a href="{% url 'users:profil' userid=machine.user.id %}" title=tr_view_the_profile>
|
||||||
<i class="fa fa-user"></i> {{ machine.user }}
|
<i class="fa fa-user"></i> {{ machine.user }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% can_create Interface machine.id %}
|
{% can_create Interface machine.id %}
|
||||||
{% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc='Ajouter une interface' %}
|
{% trans "Create an interface" as tr_create_an_interface %}
|
||||||
|
{% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc=tr_create_an_interface %}
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% history_button machine %}
|
{% history_button machine %}
|
||||||
{% can_delete machine %}
|
{% can_delete machine %}
|
||||||
|
@ -69,7 +74,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% if interface.domain.related_domain.all %}
|
{% if interface.domain.related_domain.all %}
|
||||||
{{ interface.domain }}
|
{{ interface.domain }}
|
||||||
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseDomain_{{ interface.id }}" aria-expanded="true" aria-controls="collapseDomain_{{ interface.id }}">
|
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseDomain_{{ interface.id }}" aria-expanded="true" aria-controls="collapseDomain_{{ interface.id }}">
|
||||||
Afficher les alias
|
{% trans "Display the aliases" %}
|
||||||
</button>
|
</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ interface.domain }}
|
{{ interface.domain }}
|
||||||
|
@ -87,7 +92,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% if ipv6_enabled and interface.ipv6 != 'None'%}
|
{% if ipv6_enabled and interface.ipv6 != 'None'%}
|
||||||
<b>IPv6</b>
|
<b>IPv6</b>
|
||||||
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseIpv6_{{ interface.id }}" aria-expanded="true" aria-controls="collapseIpv6_{{ interface.id }}">
|
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#collapseIpv6_{{ interface.id }}" aria-expanded="true" aria-controls="collapseIpv6_{{ interface.id }}">
|
||||||
Afficher l'IPV6
|
{% trans "Display the IPv6 address" %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -97,39 +102,44 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<button class="btn btn-primary btn-sm dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
<button class="btn btn-primary btn-sm dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
<i class="fa fa-edit"></i> <span class="caret"></span>
|
<i class="fa fa-edit"></i> <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu pull-right" aria-labelledby="editioninterface">
|
<ul class="dropdown-menu" aria-labelledby="editioninterface">
|
||||||
{% can_edit interface %}
|
{% can_edit interface %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'machines:edit-interface' interface.id %}">
|
<a href="{% url 'machines:edit-interface' interface.id %}">
|
||||||
<i class="fa fa-edit"></i> Editer
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans " Edit"%}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% can_create Domain interface.id %}
|
{% can_create Domain interface.id %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'machines:index-alias' interface.id %}">
|
<a href="{% url 'machines:index-alias' interface.id %}">
|
||||||
<i class="fa fa-edit"></i> Gerer les alias
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans " Manage the aliases" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% can_create Ipv6List interface.id %}
|
{% can_create Ipv6List interface.id %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'machines:index-ipv6' interface.id %}">
|
<a href="{% url 'machines:index-ipv6' interface.id %}">
|
||||||
<i class="fa fa-edit"></i> Gerer les ipv6
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans " Manage the IPv6 addresses" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% can_create SshFp interface.machine.id %}
|
{% can_create SshFp interface.machine.id %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'machines:index-sshfp' interface.machine.id %}">
|
<a href="{% url 'machines:index-sshfp' interface.machine.id %}">
|
||||||
<i class="fa fa-edit"></i> Manage the SSH fingerprints
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans " Manage the SSH fingerprints" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% can_create OuverturePortList %}
|
{% can_create OuverturePortList %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'machines:port-config' interface.id%}">
|
<a href="{% url 'machines:port-config' interface.id%}">
|
||||||
<i class="fa fa-edit"></i> Gerer la configuration des ports
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans " Manage the ports configuration" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
@ -142,7 +152,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% if ipv6_enabled and interface.ipv6 != 'None'%}
|
{% if ipv6_enabled and interface.ipv6 != 'None'%}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan=5 style="border-top: none; padding: 1px;">
|
<td colspan=5 style="border-top: none; padding: 1px;">
|
||||||
|
@ -156,10 +165,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if interface.domain.related_domain.all %}
|
{% if interface.domain.related_domain.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan=5 style="border-top: none; padding: 1px;">
|
<td colspan=5 style="border-top: none; padding: 1px;">
|
||||||
|
@ -176,7 +183,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -184,10 +191,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$("#machines_table").ready( function() {
|
$("#machines_table").ready( function() {
|
||||||
var alias_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.domain.related_domain.all %}$("#collapseDomain_{{ interface.id }}"), {% endif %}{% endfor %}{% endfor %}];
|
var alias_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.domain.related_domain.all %}$("#collapseDomain_{{ interface.id }}"), {% endif %}{% endfor %}{% endfor %}];
|
||||||
|
@ -207,3 +213,4 @@ $("#machines_table").ready( function() {
|
||||||
{% include "pagination.html" with list=machines_list %}
|
{% include "pagination.html" with list=machines_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type de machine</th>
|
<th>{% trans "Machine type" %}</th>
|
||||||
<th>Type d'ip correspondant</th>
|
<th>{% trans "Matching IP type" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -24,14 +24,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Zone concernée</th>
|
<th>{% trans "Concerned zone" %}</th>
|
||||||
<th>Priorité</th>
|
<th>{% trans "Priority" %}</th>
|
||||||
<th>Enregistrement</th>
|
<th>{% trans "Record" %}</th>
|
||||||
<th></th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -50,4 +50,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,15 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
{% load design %}
|
{% load design %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom</th>
|
<th>{% trans "Name" %}</th>
|
||||||
<th>Type du nas</th>
|
<th>{% trans "NAS device type" %}</th>
|
||||||
<th>Type de machine reliées au nas</th>
|
<th>{% trans "Machine type linked to the NAS device" %}</th>
|
||||||
<th>Mode d'accès</th>
|
<th>{% trans "Access mode" %}</th>
|
||||||
<th>Autocapture mac</th>
|
<th>{% trans "MAC address auto capture" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -24,13 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Zone concernée</th>
|
<th>{% trans "Concerned zone" %}</th>
|
||||||
<th>Interface autoritaire de la zone</th>
|
<th>{% trans "Authoritarian interface for the concerned zone" %}</th>
|
||||||
<th></th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -48,4 +48,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
54
machines/templates/machines/aff_role.html
Normal file
54
machines/templates/machines/aff_role.html
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load logs_extra %}
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Role name" %}</th>
|
||||||
|
<th>{% trans "Specific role" %}</th>
|
||||||
|
<th>{% trans "Servers" %}</th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for role in role_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ role.role_type }}</td>
|
||||||
|
<td>{{ role.specific_role }}</td>
|
||||||
|
<td>{% for serv in role.servers.all %}{{ serv }}, {% endfor %}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% can_edit role %}
|
||||||
|
{% include 'buttons/edit.html' with href='machines:edit-role' id=role.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
{% history_button role %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
|
@ -23,15 +23,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load design %}
|
{% load design %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom du service</th>
|
<th>{% trans "Service name" %}</th>
|
||||||
<th>Serveur</th>
|
<th>{% trans "Server" %}</th>
|
||||||
<th>Dernière régénération</th>
|
<th>{% trans "Last regeneration" %}</th>
|
||||||
<th>Régénération nécessaire</th>
|
<th>{% trans "Regeneration required" %}</th>
|
||||||
<th>Régénération activée</th>
|
<th>{% trans "Regeneration activated" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% for server in servers_list %}
|
{% for server in servers_list %}
|
||||||
|
|
|
@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom du service</th>
|
<th>{% trans "Service name" %}</th>
|
||||||
<th>Temps minimum avant nouvelle régénération</th>
|
<th>{% trans "Minimal time before regeneration" %}</th>
|
||||||
<th>Temps avant nouvelle génération obligatoire (max)</th>
|
<th>{% trans "Maximal time before regeneration" %}</th>
|
||||||
<th>Serveurs inclus</th>
|
<th>{% trans "Included servers" %}</th>
|
||||||
<th></th>
|
<th>{% trans "Ask for regeneration" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -42,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ service.min_time_regen }}</td>
|
<td>{{ service.min_time_regen }}</td>
|
||||||
<td>{{ service.regular_time_regen }}</td>
|
<td>{{ service.regular_time_regen }}</td>
|
||||||
<td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td>
|
<td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td>
|
||||||
|
<td><a role="button" class="btn btn-danger" href="{% url 'machines:regen-service' service.id %}"><i class="fa fa-sync"></i></a></td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% can_edit service %}
|
{% can_edit service %}
|
||||||
{% include 'buttons/edit.html' with href='machines:edit-service' id=service.id %}
|
{% include 'buttons/edit.html' with href='machines:edit-service' id=service.id %}
|
||||||
|
|
|
@ -24,17 +24,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nom</th>
|
<th>{% trans "Name" %}</th>
|
||||||
<th>Mail</th>
|
<th>{% trans "Mail" %}</th>
|
||||||
<th>Refresh</th>
|
<th>{% trans "Refresh" %}</th>
|
||||||
<th>Retry</th>
|
<th>{% trans "Retry" %}</th>
|
||||||
<th>Expire</th>
|
<th>{% trans "Expire" %}</th>
|
||||||
<th>TTL</th>
|
<th>{% trans "TTL" %}</th>
|
||||||
<th></th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -56,4 +56,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,19 +24,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Service</th>
|
<th>{% trans "Service" %}</th>
|
||||||
<th>Protocole</th>
|
<th>{% trans "Protocol" %}</th>
|
||||||
<th>Extension</th>
|
<th>{% trans "Extension" %}</th>
|
||||||
<th>TTL</th>
|
<th>{% trans "TTL" %}</th>
|
||||||
<th>Priorité</th>
|
<th>{% trans "Priority" %}</th>
|
||||||
<th>Poids</th>
|
<th>{% trans "Weight" %}</th>
|
||||||
<th>Port</th>
|
<th>{% trans "Port" %}</th>
|
||||||
<th>Cible</th>
|
<th>{% trans "Target" %}</th>
|
||||||
<th></th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -60,4 +60,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped long_text">
|
<table class="table table-striped long_text">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="long_text">SSH public key</th>
|
<th class="long_text">{% trans "SSH public key" %}</th>
|
||||||
<th>Algorithm used</th>
|
<th>{% trans "Algorithm used" %}</th>
|
||||||
<th>Comment</th>
|
<th>{% trans "Comment" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -42,10 +43,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% can_edit sshfp %}
|
{% can_edit sshfp %}
|
||||||
{% include 'buttons/edit.html' with href='machines:edit-sshfp' id=sshfp.id %}
|
{% include 'buttons/edit.html' with href='machines:edit-sshfp' id=sshfp.id %}
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
|
{% history_button sshfp %}
|
||||||
{% can_delete sshfp %}
|
{% can_delete sshfp %}
|
||||||
{% include 'buttons/suppr.html' with href='machines:del-sshfp' id=sshfp.id %}
|
{% include 'buttons/suppr.html' with href='machines:del-sshfp' id=sshfp.id %}
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% history_button sshfp %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -24,13 +24,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Zone concernée</th>
|
<th>{% trans "Concerned zone" %}</th>
|
||||||
<th>Enregistrement</th>
|
<th>{% trans "Record" %}</th>
|
||||||
<th></th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -48,4 +48,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load logs_extra %}
|
{% load logs_extra %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Id</th>
|
<th>{% trans "ID" %}</th>
|
||||||
<th>Nom</th>
|
<th>{% trans "Name" %}</th>
|
||||||
<th>Commentaire</th>
|
<th>{% trans "Comment" %}</th>
|
||||||
<th>Ranges ip</th>
|
<th>{% trans "IP ranges" %}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -52,3 +53,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Création et modification de machines{% endblock %}
|
{% block title %}{% trans "Creation and editing of machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h4>Attention, voulez-vous vraiment supprimer cet objet {{ objet_name }} ( {{ objet }} ) ?</h4>
|
<h4>{% blocktrans %}Warning: are you sure you want to delete this object {{ objet_name }} ( {{ objet }} )?{% endblocktrans %}</h4>
|
||||||
{% bootstrap_button "Confirmer" button_type="submit" icon="trash" %}
|
{% trans "Confirm" as tr_confirm %}
|
||||||
|
{% bootstrap_button tr_confirm button_type="submit" icon="trash" %}
|
||||||
</form>
|
</form>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Création et modification de machines{% endblock %}
|
{% block title %}{% trans "Machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% bootstrap_form_errors port_list %}
|
{% bootstrap_form_errors port_list %}
|
||||||
|
@ -46,10 +47,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<input class="btn btn-primary btn-sm" role="button" value="Ajouter un port" id="add_one">
|
{% trans "Add a port" as value %}
|
||||||
|
<input class="btn btn-primary btn-sm" role="button" value=value id="add_one">
|
||||||
</p>
|
</p>
|
||||||
|
{% trans "Create or edit" as tr_create_or_edit %}
|
||||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
{% bootstrap_button tr_create_or_edit button_type="submit" icon="star" %}
|
||||||
</form>
|
</form>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var template = `{{ports.empty_form}}`;
|
var template = `{{ports.empty_form}}`;
|
||||||
|
@ -67,3 +69,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Machines{% endblock %}
|
{% block title %}{% trans "Machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Machines</h2>
|
<h2>{% trans "Machines" %}</h2>
|
||||||
{% include "machines/aff_machines.html" with machines_list=machines_list %}
|
{% include "machines/aff_machines.html" with machines_list=machines_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -24,13 +24,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Machines{% endblock %}
|
{% block title %}{% trans "Machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Liste des alias de l'interface</h2>
|
<h2>{% trans "List of the aliases of the interface" %}</h2>
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-alias' interface_id %}"><i class="fa fa-plus"></i> Ajouter un alias</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-alias' interface_id %}"><i class="fa fa-plus"></i>{% trans " Add an alias" %}</a>
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-alias' interface_id %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs alias</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-alias' interface_id %}"><i class="fa fa-trash"></i>{% trans " Delete one or several aliases" %}</a>
|
||||||
{% include "machines/aff_alias.html" with alias_list=alias_list %}
|
{% include "machines/aff_alias.html" with alias_list=alias_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -28,57 +28,60 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Machines{% endblock %}
|
{% block title %}{% trans "Machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Liste des extensions</h2>
|
<h2>{% trans "List of extensions" %}</h2>
|
||||||
{% can_create Extension %}
|
{% can_create Extension %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-extension' %}"><i class="fa fa-plus"></i> Ajouter une extension</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-extension' %}"><i class="fa fa-plus"></i>{% trans " Add an extension" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-extension' %}"><i class="fa fa-trash"></i> Supprimer une ou plusieurs extensions</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-extension' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several extensions" %}</a>
|
||||||
{% include "machines/aff_extension.html" with extension_list=extension_list %}
|
{% include "machines/aff_extension.html" with extension_list=extension_list %}
|
||||||
|
|
||||||
<h2>Liste des enregistrements SOA</h2>
|
<h2>{% trans "List of SOA records" %}</h2>
|
||||||
{% can_create SOA %}
|
{% can_create SOA %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement SOA</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="fa fa-plus"></i>{% trans " Add an SOA record" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement SOA</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several SOA records" %}</a>
|
||||||
{% include "machines/aff_soa.html" with soa_list=soa_list %}
|
{% include "machines/aff_soa.html" with soa_list=soa_list %}
|
||||||
<h2>Liste des enregistrements MX</h2>
|
|
||||||
|
<h2>{% trans "List of MX records" %}</h2>
|
||||||
{% can_create Mx %}
|
{% can_create Mx %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement MX</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="fa fa-plus"></i>{% trans " Add an MX record" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-mx' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement MX</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-mx' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several MX records" %}</a>
|
||||||
{% include "machines/aff_mx.html" with mx_list=mx_list %}
|
{% include "machines/aff_mx.html" with mx_list=mx_list %}
|
||||||
<h2>Liste des enregistrements NS</h2>
|
|
||||||
|
<h2>{% trans "List of NS records" %}</h2>
|
||||||
{% can_create Ns %}
|
{% can_create Ns %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-ns' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement NS</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-ns' %}"><i class="fa fa-plus"></i>{% trans " Add an NS record" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-ns' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement NS</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-ns' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several NS records" %}</a>
|
||||||
{% include "machines/aff_ns.html" with ns_list=ns_list %}
|
{% include "machines/aff_ns.html" with ns_list=ns_list %}
|
||||||
<h2>Liste des enregistrements TXT</h2>
|
|
||||||
|
<h2>{% trans "List of TXT records" %}</h2>
|
||||||
{% can_create Txt %}
|
{% can_create Txt %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-txt' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement TXT</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-txt' %}"><i class="fa fa-plus"></i>{% trans " Add a TXT record" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement TXT</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several TXT records" %}</a>
|
||||||
{% include "machines/aff_txt.html" with txt_list=txt_list %}
|
{% include "machines/aff_txt.html" with txt_list=txt_list %}
|
||||||
|
|
||||||
<h2>DNAME records</h2>
|
<h2>{% trans "List of DNAME records" %}</h2>
|
||||||
{% can_create DName %}
|
{% can_create DName %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-dname' %}">
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-dname' %}">
|
||||||
<i class="fa fa-plus"></i> {% trans " Add a DNAME record" %}
|
<i class="fa fa-plus"></i> {% trans " Add a DNAME record" %}
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-dname' %}">
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-dname' %}">
|
||||||
<i class="fa fa-trash"></i> {% trans "Delete DNAME records" %}
|
<i class="fa fa-trash"></i> {% trans " Delete one or several DNAME records" %}
|
||||||
</a>
|
</a>
|
||||||
{% include "machines/aff_dname.html" with dname_list=dname_list %}
|
{% include "machines/aff_dname.html" with dname_list=dname_list %}
|
||||||
|
|
||||||
<h2>Liste des enregistrements SRV</h2>
|
<h2>{% trans "List of SRV records" %}</h2>
|
||||||
{% can_create Srv %}
|
{% can_create Srv %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-srv' %}"><i class="fa fa-plus"></i> Ajouter un enregistrement SRV</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-srv' %}"><i class="fa fa-plus"></i>{% trans " Add an SRV record" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-srv' %}"><i class="fa fa-trash"></i> Supprimer un enregistrement SRV</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-srv' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several SRV records" %}</a>
|
||||||
{% include "machines/aff_srv.html" with srv_list=srv_list %}
|
{% include "machines/aff_srv.html" with srv_list=srv_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -26,15 +26,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Ip{% endblock %}
|
{% block title %}{% trans "Machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Liste des types d'ip</h2>
|
<h2>{% trans "List of IP types" %}</h2>
|
||||||
{% can_create IpType %}
|
{% can_create IpType %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-iptype' %}"><i class="fa fa-plus"></i> Ajouter un type d'ip</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-iptype' %}"><i class="fa fa-plus"></i>{% trans " Add an IP type" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-iptype' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs types d'ip</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-iptype' %}"><i class="fa fa-trash"></i>{% trans " Delete one or several IP types" %}</a>
|
||||||
{% include "machines/aff_iptype.html" with iptype_list=iptype_list %}
|
{% include "machines/aff_iptype.html" with iptype_list=iptype_list %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -25,13 +25,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}Machines{% endblock %}
|
{% block title %}{% trans "Machines" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Liste des ipv6 de l'interface</h2>
|
<h2>{% trans "List of the IPv6 addresses of the interface" %}</h2>
|
||||||
{% can_create Ipv6List interface_id %}
|
{% can_create Ipv6List interface_id %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:new-ipv6list' interface_id %}"><i class="fa fa-plus"></i> Ajouter une ipv6</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:new-ipv6list' interface_id %}"><i class="fa fa-plus"></i>{% trans " Add an IPv6 address" %}</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% include "machines/aff_ipv6.html" with ipv6_list=ipv6_list %}
|
{% include "machines/aff_ipv6.html" with ipv6_list=ipv6_list %}
|
||||||
<br />
|
<br />
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue