Browse Source

templates & liste des paiements

Élie Bouttier 8 years ago
parent
commit
c56ab30fa9
43 changed files with 595 additions and 192 deletions
  1. 4 0
      accounts/models.py
  2. 4 4
      accounts/tests.py
  3. 4 1
      adhesions/models.py
  4. 1 1
      adhesions/templates/adhesions/_adhesion_detail.html
  5. 1 1
      adhesions/templates/adhesions/adherent.html
  6. 12 5
      adhesions/templates/adhesions/adhesion_detail.html
  7. 8 9
      adhesions/templates/adhesions/adhesion_list.html
  8. 10 9
      adhesions/templates/adhesions/corporation_detail.html
  9. 19 7
      adhesions/templates/adhesions/corporation_form.html
  10. 7 2
      adhesions/templates/adhesions/corporation_list.html
  11. 8 1
      adhesions/templates/adhesions/user_detail.html
  12. 19 7
      adhesions/templates/adhesions/user_form.html
  13. 7 2
      adhesions/templates/adhesions/user_list.html
  14. 4 4
      adhesions/tests.py
  15. 14 12
      adhesions/urls.py
  16. 0 1
      adhesions/views.py
  17. 48 47
      banking/admin.py
  18. 37 7
      banking/models.py
  19. 68 0
      banking/templates/banking/payment_detail.html
  20. 40 0
      banking/templates/banking/payment_list.html
  21. 11 0
      banking/urls.py
  22. 30 1
      banking/views.py
  23. 1 1
      djadhere/settings.py
  24. 11 0
      djadhere/templates/admin.html
  25. 9 8
      djadhere/templates/base.html
  26. 2 1
      djadhere/urls.py
  27. 21 0
      services/migrations/0013_auto_20170417_1640.py
  28. 13 2
      services/models.py
  29. 2 2
      services/templates/services/_service_list.html
  30. 8 3
      services/templates/services/ipresource_detail.html
  31. 2 2
      services/templates/services/ipresource_form.html
  32. 14 4
      services/templates/services/ipresource_list.html
  33. 20 5
      services/templates/services/resourceallocation_form.html
  34. 6 4
      services/templates/services/service_detail.html
  35. 37 0
      services/templates/services/service_detail_user.html
  36. 18 5
      services/templates/services/service_form.html
  37. 6 2
      services/templates/services/service_list.html
  38. 6 4
      services/templates/services/servicetype_detail.html
  39. 18 5
      services/templates/services/servicetype_form.html
  40. 6 2
      services/templates/services/servicetype_list.html
  41. 1 1
      services/tests.py
  42. 16 13
      services/urls.py
  43. 22 7
      services/views.py

+ 4 - 0
accounts/models.py

@@ -1,6 +1,7 @@
 from django.db import models
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.models import ContentType
+from django.urls import reverse
 
 
 from adhesions.models import Adhesion, Corporation
@@ -33,5 +34,8 @@ class Profile(models.Model):
             | models.Q(adherent_type=corp_type, adherent_id__in=Corporation.objects.filter(members=self.user).values_list('pk'))
         )
 
+    def get_absolute_url(self):
+        return reverse('user-detail', kwargs={'pk': self.user.pk})
+
     def __str__(self):
         return self.user.get_full_name() or self.user.username

+ 4 - 4
accounts/tests.py

@@ -6,7 +6,7 @@ from .models import Profile
 from .forms import UserChangeForm, ProfileForm
 
 
-class AccountsTests(TestCase):
+class ViewsTestCase(TestCase):
     def setUp(self):
         user = User.objects.create_user('user', first_name='first', last_name='last', email='user@example.net', password='user')
 
@@ -23,9 +23,9 @@ class AccountsTests(TestCase):
     def test_login_logout(self):
         self.assertEquals(self.client.get(reverse('login')).status_code, 200)
         self.client.login(username='user', password='user')
-        self.assertEquals(self.client.get(reverse('user')).status_code, 200)
-        self.assertRedirects(self.client.get(reverse('logout')), reverse('user'), target_status_code=302) # user page is redirected to login
-        self.assertRedirects(self.client.get(reverse('user')), reverse('login') + '?next=' + reverse('user'))
+        self.assertEquals(self.client.get(reverse('adhesion-detail-user')).status_code, 200)
+        self.assertRedirects(self.client.get(reverse('logout')), reverse('adhesion-detail-user'), target_status_code=302) # user page is redirected to login
+        self.assertRedirects(self.client.get(reverse('adhesion-detail-user')), reverse('login') + '?next=' + reverse('adhesion-detail-user'))
 
     def test_profile(self):
         response = self.client.get(reverse('profile'))

+ 4 - 1
adhesions/models.py

@@ -31,7 +31,7 @@ class Adhesion(models.Model):
     @property
     def contribution(self):
         try:
-            return self.contributions.filter(get_active_filter()).get()
+            return self.contributions.exclude(period=0).filter(get_active_filter()).get()
         except Payment.DoesNotExist:
             return None
         # MultipleObjectsReturned non catché volontairement, le filtrage par la méthode clean est censé
@@ -69,6 +69,9 @@ class Adhesion(models.Model):
         else:
             return reverse('corporation-edit', kwargs={'pk': self.adherent.pk})
 
+    def get_absolute_url(self):
+        return reverse('adhesion-detail', kwargs={'pk': self.pk})
+
     def __str__(self):
         if self.id:
             return 'ADT%d (%s)' % (self.id, self.get_adherent_name())

+ 1 - 1
adhesions/templates/adhesions/_adhesion_detail.html

@@ -2,7 +2,7 @@
 
 <div class="panel panel-primary">
     <div class="panel-heading">
-        <h3>Adhésion</h3>
+        <h4>Adhésion</h4>
     </div>
     <div class="panel-body">
         {% if adhesion %}

+ 1 - 1
adhesions/templates/adhesions/adherent.html

@@ -6,7 +6,7 @@
 
 {% include 'adhesions/_adhesion_detail.html' %}
 
-{% with services=adhesion.services.all %}
+{% with services=adhesion.services.all service_detail_url='service-detail-user' %}
 {% include 'services/_service_list.html' %}
 {% endwith %}
 

+ 12 - 5
adhesions/templates/adhesions/adhesion_detail.html

@@ -1,9 +1,16 @@
-{% extends 'adhesions/adherent.html' %}
+{% extends 'admin.html' %}
 
-{% block header %}
-<a class="btn btn-primary" href="{% url 'adhesion-list' %}"><span class="glyphicon glyphicon-arrow-left"></span>&nbsp;Retour à la liste des adhérents</a>
+{% block breadcrumb %}
+<li><a href="{% url 'adhesion-list' %}">Adhérents</a></li>
+<li class="active">{{ adhesion }}</li>
+{% endblock %}
+
+{% block content %}
+
+{% include 'adhesions/_adhesion_detail.html' %}
 
-<br />
+{% with services=adhesion.services.all service_detail_url='service-detail' %}
+{% include 'services/_service_list.html' %}
+{% endwith %}
 
-<h1>{{ adhesion }}</h1>
 {% endblock %}

+ 8 - 9
adhesions/templates/adhesions/adhesion_list.html

@@ -1,18 +1,17 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-{% comment %}
-
-<br /><br />
-{% endcomment %}
+{% block breadcrumb %}
+<li class="active">Adhérents</li>
+{% endblock %}
 
+{% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
         <div class="pull-right">
             <a class="btn btn-success" href="{% url 'user-add' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;Ajouter un utilisateur</a>
             <a class="btn btn-success" href="{% url 'corporation-add' %}"><span class="glyphicon glyphicon-globe"></span>&nbsp;Ajouter une association</a>
         </div>
-        <h1>Adhérents</h1>
+        <h4>Adhérents</h4>
     </div>
     {% for adhesion in object_list %}
     {% if forloop.first %}
@@ -21,14 +20,14 @@
             <th>Numéro d’adhérent</th>
             <th>Nom ou raison social</th>
             <th>Type</th>
-            <th></th>
+            {% comment %}<th></th>{% endcomment %}
         </tr>
     {% endif %}
         <tr>
             <td><a href="{% url 'adhesion-detail' adhesion.id %}">ADT{{ adhesion.id }}</a></td>
             <td><a href="{{ adhesion.get_adherent_detail_url }}">{{ adhesion.get_adherent_name }}</a></td>
             <td>{{ adhesion.type }}</td>
-            <td class="text-right"><a href="{{ adhesion.get_adherent_edit_url }}"><span class="glyphicon glyphicon-pencil"></span>&nbsp;Modifier</a></td>
+            {% comment %}<td class="text-right"><a href="{{ adhesion.get_adherent_edit_url }}"><span class="glyphicon glyphicon-pencil"></span>&nbsp;Modifier</a></td>{% endcomment %}
         </tr>
     {% if forloop.last %}
     </table>

+ 10 - 9
adhesions/templates/adhesions/corporation_detail.html

@@ -1,14 +1,16 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-
-<a class="btn btn-primary" href="{% url 'corporation-list' %}"><span class="glyphicon glyphicon-arrow-left"></span>&nbsp;Retour à la liste des personnes morales</a>
-
-<br /><br />
+{% block breadcrumb %}
+{{ block.super }}
+<li><a href="{% url 'corporation-list' %}">Associations</a></li>
+<li class="active">{{ corporation }}</li>
+{% endblock %}
 
+{% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
-        {{ corporation }}
+        <a href="{% url 'corporation-edit' corporation.pk %}" class="btn btn-success pull-right">Éditer</a>
+        <h4>{{ corporation }}</h4>
     </div>
     <table class="table table-bordered">
         <tr>
@@ -26,7 +28,7 @@
 
 <div class="panel panel-primary">
     <div class="panel-heading">
-        Membres
+        <h4>Membres</h4>
     </div>
     <table class="table table-bordered">
         {% for user in corporation.members.all %}
@@ -55,5 +57,4 @@
         {% endfor %}
     </table>
 </div>
-
 {% endblock %}

+ 19 - 7
adhesions/templates/adhesions/corporation_form.html

@@ -1,11 +1,23 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-
-<a class="btn btn-primary pull-right" href="{% url 'corporation-list' %}"><span class="glyphicon glyphicon-arrow-left"></span>&nbsp;Retour à la liste des associations</a>
-
-<h1>{% if adhesion %}Modifier{% else %}Ajouter{% endif %} une association</h1>
+{% block breadcrumb %}
+{{ block.super }}
+<li><a href="{% url 'corporation-list' %}">Associations</a></li>
+{% if corporation %}
+<li><a href="{{ corporation.get_absolute_url }}">{{ corporation }}</a></li>
+<li class="active">Modification</li>
+{% else %}
+<li class="active">Nouvelle</li>
+{% endif %}
+{% endblock %}
 
+{% block content %}
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        <h4>{% if corporation %}Modifier{% else %}Ajouter{% endif %} une association</h4>
+    </div>
+    <div class="panel-body">
 {% include '_form.html' %}
-
+    </div>
+</div>
 {% endblock %}

+ 7 - 2
adhesions/templates/adhesions/corporation_list.html

@@ -1,10 +1,15 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<li class="active">Associations</li>
+{% endblock %}
 
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
         <a class="btn btn-success pull-right" href="{% url 'corporation-add' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;Ajouter une association</a>
-        <h1>Associations</h1>
+        <h4>Associations</h4>
     </div>
     {% for corp in object_list %}
     {% if forloop.first %}

+ 8 - 1
adhesions/templates/adhesions/user_detail.html

@@ -1,8 +1,15 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<li><a href="{% url 'user-list' %}">Utilisateurs</a></li>
+<li class="active">{{ user.profile }}</li>
+{% endblock %}
 
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
+        <a href="{% url 'user-edit' user.pk %}" class="btn btn-success pull-right">Éditer</a>
         <h4>{{ user.profile }}</h4>
     </div>
     <table class="table table-bordered">

+ 19 - 7
adhesions/templates/adhesions/user_form.html

@@ -1,11 +1,23 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-
-<a class="btn btn-primary pull-right" href="{% url 'user-list' %}"><span class="glyphicon glyphicon-arrow-left"></span>&nbsp;Retour à la liste des utilisateurs</a>
-
-<h1>{% if user %}Modifier{% else %}Ajouter{% endif %} un utilisateur</h1>
+{% block breadcrumb %}
+{{ block.super }}
+<li><a href="{% url 'user-list' %}">Utilisateurs</a></li>
+{% if user %}
+<li><a href="{{ user.profile.get_absolute_url }}">{{ user.profile }}</a></li>
+<li class="active">Modification</li>
+{% else %}
+<li class="active">Nouveau</li>
+{% endif %}
+{% endblock %}
 
+{% block content %}
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        <h4>{% if user %}Modifier{% else %}Ajouter{% endif %} un utilisateur</h4>
+    </div>
+    <div class="panel-body">
 {% include '_form.html' %}
-
+    </div>
+</div>
 {% endblock %}

+ 7 - 2
adhesions/templates/adhesions/user_list.html

@@ -1,10 +1,15 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<li class="active">Utilisateurs</li>
+{% endblock %}
 
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
         <a class="btn btn-success pull-right" href="{% url 'user-add' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;Ajouter un utilisateur</a>
-        <h1>Utilisateurs</h1>
+        <h4>Utilisateurs</h4>
     </div>
     {% for user in object_list %}
     {% if forloop.first %}

+ 4 - 4
adhesions/tests.py

@@ -34,16 +34,16 @@ class ViewsTestCase(AdhesionsMixin, TestCase):
         self.assertTrue(self.client.login(username='ADT%d' % adhesion.pk, password='user'))
 
     def test_user(self):
-        response = self.client.get(reverse('user'))
-        self.assertRedirects(response, reverse('login') + '?next=' + reverse('user'))
+        response = self.client.get(reverse('adhesion-detail-user'))
+        self.assertRedirects(response, reverse('login') + '?next=' + reverse('adhesion-detail-user'))
         self.client.login(username='user', password='user')
-        response = self.client.get(reverse('user'))
+        response = self.client.get(reverse('adhesion-detail-user'))
         self.assertEqual(response.status_code, 200)
 
     def test_corporation(self):
         user = User.objects.get(username='user')
         corp = Corporation.objects.get(social_reason='EvilCorp')
-        url = reverse('corporation', kwargs={'pk': corp.pk})
+        url = reverse('corporation-detail-user', kwargs={'pk': corp.pk})
         response = self.client.get(url)
         self.assertRedirects(response, reverse('login') + '?next=' + url)
         self.client.login(username='user', password='user')

+ 14 - 12
adhesions/urls.py

@@ -4,16 +4,18 @@ from . import views
 
 
 urlpatterns = [
-    url(r'^$', views.user, name='user'),
-    url(r'^asso/(?P<pk>[0-9]+)/$', views.corporation, name='corporation'),
-    url(r'^users/$', views.UserList.as_view(), name='user-list'),
-    url(r'^users/add/$', views.user_edit, name='user-add'),
-    url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view(), name='user-detail'),
-    url(r'^users/(?P<pk>[0-9]+)/edit/$', views.user_edit, name='user-edit'),
-    url(r'^corporations/$', views.CorporationList.as_view(), name='corporation-list'),
-    url(r'^corporations/add/$', views.CorporationCreate.as_view(), name='corporation-add'),
-    url(r'^corporations/(?P<pk>[0-9]+)/', views.CorporationDetail.as_view(), name='corporation-detail'),
-    url(r'^corporations/(?P<pk>[0-9]+)/edit/', views.CorporationUpdate.as_view(), name='corporation-edit'),
-    url(r'^adhesions/$', views.AdhesionList.as_view(), name='adhesion-list'),
-    url(r'^adhesions/(?P<pk>[0-9]+)/$', views.AdhesionDetail.as_view(), name='adhesion-detail'),
+    # User views
+    url(r'^$', views.user, name='adhesion-detail-user'),
+    url(r'^asso/(?P<pk>[0-9]+)/$', views.corporation, name='corporation-detail-user'),
+    # Admin views
+    url(r'^admin/users/$', views.UserList.as_view(), name='user-list'),
+    url(r'^admin/users/add/$', views.user_edit, name='user-add'),
+    url(r'^admin/users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view(), name='user-detail'),
+    url(r'^admin/users/(?P<pk>[0-9]+)/edit/$', views.user_edit, name='user-edit'),
+    url(r'^admin/corporations/$', views.CorporationList.as_view(), name='corporation-list'),
+    url(r'^admin/corporations/add/$', views.CorporationCreate.as_view(), name='corporation-add'),
+    url(r'^admin/corporations/(?P<pk>[0-9]+)/$', views.CorporationDetail.as_view(), name='corporation-detail'),
+    url(r'^admin/corporations/(?P<pk>[0-9]+)/edit/$', views.CorporationUpdate.as_view(), name='corporation-edit'),
+    url(r'^admin/adhesions/$', views.AdhesionList.as_view(), name='adhesion-list'),
+    url(r'^admin/adhesions/(?P<pk>[0-9]+)/$', views.AdhesionDetail.as_view(), name='adhesion-detail'),
 ]

+ 0 - 1
adhesions/views.py

@@ -10,7 +10,6 @@ from django.contrib import messages
 
 from accounts.models import Profile
 from accounts.forms import UserCreateForm, UserChangeForm, ProfileForm
-#from banking.forms import PaymentForm
 
 from .models import Adhesion, Corporation
 from .forms import AdhesionForm

+ 48 - 47
banking/admin.py

@@ -57,48 +57,49 @@ class PaymentInline(GenericTabularInline):
 
 ### Filters
 
-#class PaymentTypeFilter(admin.SimpleListFilter):
-#    title = 'type de paiement'
-#    parameter_name = 'type'
-#
-#    def lookups(self, request, model_admin):
-#        choices = [
-#            ('membership', 'Cotisation'),
-#            ('service', 'Service'),
-#        ]
-#        service_types = ServiceType.objects.all()
-#        if not (request.user.is_superuser or request.user.has_perm('banking.validate_payment')):
-#            service_types = service_types.filter(group__in=request.user.groups.all())
-#        for stype in service_types:
-#            choices.append((stype.pk, 'Service (%s)' % stype.name))
-#        return choices
-#
-#    def queryset(self, request, queryset):
-#        if self.value() == 'membership':
-#            return queryset.filter(reason_type__app_label='adhesions',
-#                                   reason_type__model='adherent')
-#        if self.value() == 'service':
-#            return queryset.filter(reason_type__app_label='services',
-#                                   reason_type__model='service')
-#        try:
-#            service_type = ServiceType.objects.get(pk=int(self.value()))
-#        except (ValueError, TypeError, ServiceType.DoesNotExist,):
-#            return queryset
-#        else:
-#            return queryset.filter(service__service_type=service_type)
-#
-#
+class PaymentTypeFilter(admin.SimpleListFilter):
+    title = 'type de paiement'
+    parameter_name = 'type'
+
+    def lookups(self, request, model_admin):
+        choices = [
+            ('membership', 'Cotisation'),
+            ('service', 'Service'),
+        ]
+        service_types = ServiceType.objects.all()
+        if not (request.user.is_superuser or request.user.has_perm('banking.validate_payment')):
+            service_types = service_types.filter(group__in=request.user.groups.all())
+        for stype in service_types:
+            choices.append((stype.pk, 'Service (%s)' % stype.name))
+        return choices
+
+    def queryset(self, request, queryset):
+        if self.value() == 'membership':
+            return queryset.filter(reason_type__app_label='adhesions',
+                                   reason_type__model='adhesion')
+        if self.value() == 'service':
+            return queryset.filter(reason_type__app_label='services',
+                                   reason_type__model='service')
+        try:
+            service_type = ServiceType.objects.get(pk=int(self.value()))
+        except (ValueError, TypeError, ServiceType.DoesNotExist,):
+            return queryset
+        else:
+            return queryset.filter(service__service_type=service_type)
+
+
 #### Actions
 #
 #def validate_payment(payment, request, queryset):
 #    queryset.update(validated=True)
-#
-#
-#### ModelAdmin
-#
-#class PaymentAdmin(admin.ModelAdmin):
+
+
+### ModelAdmin
+
+class PaymentAdmin(admin.ModelAdmin):
+    list_display = ('id', 'type_verbose', 'get_adhesion', 'amount', 'period', 'start_display', 'end_display',)
 #    #list_display_links = None
-#    list_filter = (PaymentTypeFilter, 'payment_method', 'validated',)
+    list_filter = (PaymentTypeFilter, 'payment_method', 'validated',)
 #
 #    def get_list_display(self, request):
 #        list_display = ()
@@ -114,13 +115,13 @@ class PaymentInline(GenericTabularInline):
 #        #    list_display += ('change_pending',)
 #        return list_display
 #
-#    def start_display(self, obj):
-#        return obj.start
-#    start_display.short_description = 'Début'
-#
-#    def end_display(self, obj):
-#        return obj.end
-#    end_display.short_description = 'Fin'
+    def start_display(self, obj):
+        return obj.start
+    start_display.short_description = 'Début'
+
+    def end_display(self, obj):
+        return obj.end
+    end_display.short_description = 'Fin'
 #
 #    def validated_display(self, obj):
 #        return obj.validated
@@ -166,6 +167,6 @@ class PaymentInline(GenericTabularInline):
 #        if obj and not request.user.has_perm('banking.validate_payment'):
 #            return not obj.validated
 #        return False
-#
-#
-#admin.site.register(Payment, PaymentAdmin)
+
+
+admin.site.register(Payment, PaymentAdmin)

+ 37 - 7
banking/models.py

@@ -4,8 +4,11 @@ from django.contrib.contenttypes.models import ContentType
 from django.core.validators import MaxValueValidator
 from django.core.urlresolvers import reverse
 from django.core.exceptions import ValidationError
+from django.urls import reverse
+from django.core.exceptions import PermissionDenied
+from django.utils import timezone
 
-from djadhere.utils import is_overlapping
+from djadhere.utils import is_overlapping, get_active_filter
 
 
 class Payment(models.Model):
@@ -15,8 +18,10 @@ class Payment(models.Model):
         (TRANSFERT, 'Virement'),
         (WITHDRAWAL, 'Prélèvement'),
     )
-    limit = models.Q(app_label='adhesions', model='Adhesion') \
-        | models.Q(app_label='services', model='Service')
+    ADHESION = 0
+    SERVICE = 1
+    limit = models.Q(app_label='adhesions', model='adhesion') \
+        | models.Q(app_label='services', model='service')
     reason_type = models.ForeignKey(ContentType, on_delete=models.CASCADE,
                                     limit_choices_to=limit)
     reason_id = models.PositiveIntegerField()
@@ -36,14 +41,30 @@ class Payment(models.Model):
             ('validate_payment', 'Peut valider les paiements'),
         )
 
-    def payment_type_verbose(self):
+    @property
+    def active(self):
+        # Contrairement aux allocations d’IP, un paiement est considéré actif même s’il commence dans le futur
+        # Seul les paiements récurrents peuvent être actifs
+        if self.period == 0:
+            return None
+        today = timezone.localtime(timezone.now()).date()
+        return self.end is None or (self.start <= today and self.end > today)
+
+    @property
+    def type(self):
         if self.reason_type.app_label == 'adhesions' \
                 and self.reason_type.model == 'adhesion':
-            return 'Cotisation'
+            return Payment.ADHESION
         if self.reason_type.app_label == 'services' \
                 and self.reason_type.model == 'service':
-            return 'Service (%s)' % self.reason.service_type
-    payment_type_verbose.short_description = 'Type'
+            return Payment.SERVICE
+
+    def type_verbose(self):
+        if self.type == Payment.ADHESION:
+            return 'Cotisation'
+        if self.type == Payment.SERVICE:
+            return 'Service'
+    type_verbose.short_description = 'Type'
 
     def period_verbose(self):
         if self.period == 0:
@@ -73,6 +94,13 @@ class Payment(models.Model):
             return self.reason.adhesion
     get_adhesion.short_description = 'Adhésion'
 
+    # Penser à appele la méthode save !
+    def stop(self):
+        if not self.active:
+            raise PermissionDenied
+        today = timezone.localtime(timezone.now()).date()
+        self.end = max(self.start, today)
+
     def clean(self):
         super().clean()
         # S’il s’agit d’un paiement non récurrent, le champ end doit rester blanc
@@ -88,6 +116,8 @@ class Payment(models.Model):
             if is_overlapping(self, payments):
                 raise ValidationError("Les périodes de paiement ne doivent pas se chevaucher.")
 
+    def get_absolute_url(self):
+        return reverse('payment-detail', kwargs={'pk': self.pk})
 
     def __str__(self):
         s = str(self.amount) + '€'

+ 68 - 0
banking/templates/banking/payment_detail.html

@@ -0,0 +1,68 @@
+{% extends 'admin.html' %}
+
+{% block breadcrumb %}
+<li><a href="{% url 'payment-list' %}">Paiements</a></li>
+<li class="active">{{ payment.payment_type_verbose }} {{ payment.reason }}</li>
+{% endblock %}
+
+{% load bootstrap3 %}
+
+{% block content %}
+<div class="panel panel-{{ payment.active|yesno:"success,danger,primary"}}">
+    <div class="panel-heading">
+        {% comment %}{% if perms.services.change_service %}
+        <a class="btn btn-success pull-right" href="{% url 'service-edit' service.pk %}"><span class="glyphicon glyphicon-edit"></span>&nbsp;Éditer</a>
+        {% endif %}
+        {% endcomment %}
+        <h4>{{ payment.payment_type_verbose }} {{ payment.reason }}</h4>
+    </div>
+    <div class="panel-body container">
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Type</strong></div>
+            <div class="col-md-9">{{ payment.type_verbose }}</div>
+        </div>
+        {% if payment.type == payment.SERVICE %}
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Service</strong></div>
+            <div class="col-md-9"><a href="{{ payment.reason.get_absolute_url }}">{{ payment.reason }}</a></div>
+        </div>
+        {% endif %}
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Adhérent</strong></div>
+            <div class="col-md-9"><a href="{{ payment.get_adhesion.get_absolute_url }}">{{ payment.get_adhesion }}</a></div>
+        </div>
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Période</strong></div>
+            <div class="col-md-9">{{ payment.period_verbose }}</div>
+        </div>
+        {% if payment.period %}
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Status</strong></div>
+            <div class="col-md-9">
+                {% if payment.active %}
+                en cours
+                <a href="{% url 'payment-stop' payment.pk %}" class="btn btn-danger btn-xs">Stopper le paiement</a>
+                {% else %}
+                terminé
+                {% endif %}
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Premier paiement</strong></div>
+            <div class="col-md-9">{{ payment.start }}</div>
+        </div>
+        {% if payment.end %}
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Dernier paiement</strong></div>
+            <div class="col-md-9">{{ payment.end }}</div>
+        </div>
+        {% endif %}
+        {% else %}
+        <div class="row">
+            <div class="col-md-3 text-right"><strong>Date du paiement</strong></div>
+            <div class="col-md-9">{{ payment.start }}</div>
+        </div>
+        {% endif %}
+    </div>
+</div>
+{% endblock %}

+ 40 - 0
banking/templates/banking/payment_list.html

@@ -0,0 +1,40 @@
+{% extends 'admin.html' %}
+
+{% block breadcrumb %}
+<li class="active">Paiements</li>
+{% endblock %}
+
+{% load bootstrap3 %}
+
+{% block content %}
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        {% comment %}<a class="btn btn-success pull-right" href="{% url 'service-add' %}">Ajouter une cotisation</a>{% endcomment %}
+        <h4>Liste des paiements</h4>
+    </div>
+    {% for payment in object_list %}
+    {% if forloop.first %}
+    <table class="table">
+        <tr>
+            <th>#</th>
+            <th>Type</th>
+            <th>Objet</th>
+            <th>Montant</th>
+        </tr>
+    {% endif %}
+        <tr class="{{ payment.active|yesno:"success,danger," }}">
+            <td><a href="{{ payment.get_absolute_url }}">#{{ payment.pk }}</a></td>
+            <td>{{ payment.type_verbose }}</td>
+            <td><a href="{{ payment.reason.get_absolute_url }}">{{ payment.reason }}</a></td>
+            <td>{{ payment }}</td>
+        </tr>
+    {% if forloop.last %}
+    </table>
+    {% endif %}
+    {% empty %}
+    <div class="panel-body">
+        <em>Aucun paiement.</em>
+    </div>
+    {% endfor %}
+</div>
+{% endblock %}

+ 11 - 0
banking/urls.py

@@ -0,0 +1,11 @@
+from django.conf.urls import url, include
+
+from . import views
+
+
+urlpatterns = [
+    # Admin views
+    url(r'^admin/paiements/$', views.PaymentList.as_view(), name='payment-list'),
+    url(r'^admin/paiements/(?P<pk>[0-9]+)/$', views.PaymentDetail.as_view(), name='payment-detail'),
+    url(r'^admin/paiements/(?P<pk>[0-9]+)/stop/$', views.PaymentStop.as_view(), name='payment-stop'),
+]

+ 30 - 1
banking/views.py

@@ -1,3 +1,32 @@
 from django.shortcuts import render
+from django.views.generic import ListView, DetailView, RedirectView
+from django.views.generic.detail import SingleObjectMixin
+from django.contrib.auth.mixins import PermissionRequiredMixin
 
-# Create your views here.
+from .models import Payment
+
+
+class PaymentMixin(PermissionRequiredMixin):
+    model = Payment
+    permission_required = 'banking.change_payment'
+
+
+class PaymentList(PaymentMixin, ListView):
+    pass
+
+
+class PaymentDetail(PaymentMixin, DetailView):
+    pass
+
+
+class PaymentStop(PaymentMixin, SingleObjectMixin, RedirectView):
+    #http_method_names = ['post']
+
+    def get_object(self, queryset=None):
+        obj = super().get_object(queryset)
+        obj.stop()
+        obj.save()
+        return obj
+
+    def get_redirect_url(self, *args, **kwargs):
+        return self.get_object().get_absolute_url()

+ 1 - 1
djadhere/settings.py

@@ -136,7 +136,7 @@ USE_TZ = True
 
 STATIC_URL = '/static/'
 
-LOGOUT_REDIRECT_URL = 'user'
+LOGOUT_REDIRECT_URL = 'adhesion-detail-user'
 
 BOOTSTRAP3 = {
 

+ 11 - 0
djadhere/templates/admin.html

@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load bootstrap3 staticfiles %} 
+
+{% block managetab %} active{% endblock %}
+
+{% block breadcrumbol %}
+<ol class="breadcrumb">
+{% block breadcrumb %}
+{% endblock %}
+</ol>
+{% endblock %}

+ 9 - 8
djadhere/templates/base.html

@@ -14,7 +14,6 @@
 {% endblock %}
 
 {% block body %}
-
 {% block navbar %}
     <nav class="navbar navbar-default" role="navigation">
       <div class="container">
@@ -29,7 +28,7 @@
         </div>
         <div id="navbar" class="navbar-collapse collapse">
           <ul class="nav navbar-nav">
-            <li{% block usertab %}{% endblock %}><a href="{% url 'user' %}"><span class="glyphicon glyphicon-heart-empty"></span>&nbsp;Mon adhésion</a></li>
+            <li{% block usertab %}{% endblock %}><a href="{% url 'adhesion-detail-user' %}"><span class="glyphicon glyphicon-heart-empty"></span>&nbsp;Mon adhésion</a></li>
             {% for corp in request.corporations %}
             {% if forloop.first %}
             <li class="dropdown{% block corptab %}{% endblock %}">
@@ -38,7 +37,7 @@
                 </a>
                 <ul class="dropdown-menu">
             {% endif %}
-                    <li><a href="{% url 'corporation' corp.object.pk %}">{{ corp.object.social_reason }}</a></li>
+                    <li><a href="{% url 'corporation-detail-user' corp.object.pk %}">{{ corp.object.social_reason }}</a></li>
             {% if forloop.last %}
                 </ul>
             </li>
@@ -61,6 +60,7 @@
                     <li><a href="{% url 'service-list' %}"><span class="glyphicon glyphicon-tasks"></span>&nbsp;Services</a></li>
                     <li><a href="{% url 'servicetype-list' %}"><span class="glyphicon glyphicon-king"></span>&nbsp;Types de services</a></li>
                     <li><a href="{% url 'ip-list' %}"><span class="glyphicon glyphicon-asterisk"></span>&nbsp;IP</a></li>
+                    <li><a href="{% url 'payment-list' %}"><span class="glyphicon glyphicon-piggy-bank"></span>&nbsp;Paiements</a></li>
                 </ul>
             </li>
             {% endif %}
@@ -76,13 +76,14 @@
 	<div class="container">
       <div class="row">
         <div id="content" class="col-md-12">
-        {% bootstrap_messages %}
+{% block breadcrumbol %}{% endblock %}
+          {% bootstrap_messages %}
 {% block content %}{% endblock %}
 {% block pagefooter %}
-        <hr>
-        <footer>
-          <p class="text-muted">Propulsé par <a href="https://code.ffdn.org/tetaneutral.net/djadhere">djadhere</a></p>
-        </footer>
+          <hr>
+          <footer>
+            <p class="text-muted">Propulsé par <a href="https://code.ffdn.org/tetaneutral.net/djadhere">djadhere</a></p>
+          </footer>
 {% endblock %}
         </div>
       </div>

+ 2 - 1
djadhere/urls.py

@@ -21,7 +21,8 @@ urlpatterns = [
     url(r'^accounts/', include('accounts.urls')),
     url(r'^', include('services.urls')),
     url(r'^', include('adhesions.urls')),
-    url(r'^admin/', admin.site.urls),
+    url(r'^', include('banking.urls')),
+    url(r'^djangoadmin/', admin.site.urls),
 ]
 
 admin.site.site_title = 'tetaneutral.net'

+ 21 - 0
services/migrations/0013_auto_20170417_1640.py

@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-04-17 14:40
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('services', '0012_auto_20170415_2307'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='resourceallocation',
+            name='start',
+            field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Début de la période d’allocation'),
+        ),
+    ]

+ 13 - 2
services/models.py

@@ -6,6 +6,8 @@ from django.contrib.auth.models import Group
 from django.contrib.contenttypes.fields import GenericRelation
 from django.core.exceptions import ValidationError
 from django.urls import reverse
+from django.utils import timezone
+from django.core.exceptions import PermissionDenied
 
 from djadhere.utils import get_active_filter, is_overlapping
 from adhesions.models import Adhesion
@@ -32,6 +34,9 @@ class IPResource(models.Model):
         verbose_name = 'ressource IP'
         verbose_name_plural = 'ressources IP'
 
+    def get_absolute_url(self):
+        return reverse('ip-detail', kwargs={'pk': self.pk})
+
     def __str__(self):
         r = str(self.ip)
         if self.mask:
@@ -72,7 +77,7 @@ class Service(models.Model):
     @property
     def contribution(self):
         try:
-            return self.contributions.get(get_active_filter())
+            return self.contributions.exclude(period=0).get(get_active_filter())
         except Payment.DoesNotExist:
             return None
         # MultipleObjectsReturned non catché volontairement, cf remarque adhesions.Adhesion.contribution
@@ -104,7 +109,7 @@ class Service(models.Model):
 class ResourceAllocation(models.Model):
     resource = models.ForeignKey(IPResource, verbose_name='Ressource', related_name='allocations', related_query_name='allocation')
     service = models.ForeignKey(Service, related_name='allocations', related_query_name='allocation')
-    start = models.DateTimeField(verbose_name='Début de la période d’allocation')
+    start = models.DateTimeField(verbose_name='Début de la période d’allocation', default=timezone.now)
     end = models.DateTimeField(null=True, blank=True, verbose_name='Fin de la période d’allocation')
 
     @property
@@ -124,6 +129,12 @@ class ResourceAllocation(models.Model):
                 raise ValidationError("La période d’allocation de cette ressource chevauche "
                                         "avec une période d’allocation précédente.")
 
+    # Penser à appeler la méthode save !
+    def deallocate(self):
+        if not self.active:
+            raise PermissionDenied
+        self.end = timezone.now()
+
     class Meta:
         verbose_name = 'allocation'
         verbose_name_plural = 'allocations'

+ 2 - 2
services/templates/services/_service_list.html

@@ -4,7 +4,7 @@
 {% if forloop.first %}
 <div class="panel panel-primary">
     <div class="panel-heading">
-        <h3 class="list-group-item-heading">Services</h3>
+        <h4 class="list-group-item-heading">Services</h4>
         <p>
             Services actifs : {{ services|active|count }}<br />
             Services inactifs : {{ services|inactive|count }}
@@ -12,7 +12,7 @@
     </div>
     <div class="list-group">
 {% endif %}
-    <a href="{% url 'service-detail' service.pk %}" class="list-group-item {% if service.active %}list-group-item-success{% else %}list-group-item-danger{% endif %}">
+    <a href="{% url service_detail_url service.pk %}" class="list-group-item {% if service.active %}list-group-item-success{% else %}list-group-item-danger{% endif %}">
         <span class="badge">#{{ service.id }}</span>
         <h4 class="list-group-item-heading">
             <b>{{ service.service_type }}</b>

+ 8 - 3
services/templates/services/ipresource_detail.html

@@ -1,13 +1,18 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
 {% load bootstrap3 services %}
 
+{% block breadcrumb %}
+<li><a href="{% url 'ip-list' %}">IP</a></li>
+<li class="active">{{ ipresource }}</li>
+{% endblock %}
+
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
         {% if not ipresource.in_use %}<a class="btn btn-success pull-right" href="{% url 'ip-allocate' ipresource.pk %}">Allouer</a>{% endif %}
-        <h3>{{ ipresource }}</h3>
-        <h4>Historique des allocations</h4>
+        <h4>{{ ipresource }}</h4>
+        <h5>Historique des allocations</h5>
     </div>
     {% for allocation in ipresource.allocations.all %}
     {% if forloop.first %}

+ 2 - 2
services/templates/services/ipresource_form.html

@@ -1,4 +1,4 @@
-{% extends 'base.html' %}
+{% comment %}{% extends 'base.html' %}
 
 {% block content %}
 
@@ -6,4 +6,4 @@
 
 {% include '_form.html' %}
 
-{% endblock %}
+{% endblock %}{% endcomment %}

+ 14 - 4
services/templates/services/ipresource_list.html

@@ -1,12 +1,15 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
+
+{% block breadcrumb %}
+<li class="active">IP</li>
+{% endblock %}
 
 {% load bootstrap3 services %}
 
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
-        {% comment %}<a class="btn btn-success pull-right" href="{% url 'ip-add' %}">Ajouter une ressource IP</a>{% endcomment %}
-        <h1>Ressources IP</h1>
+        <h4>Liste des IP</h4>
     </div>
     {% for ip in object_list %}
     {% if forloop.first %}
@@ -14,13 +17,20 @@
         <tr>
             <th>Adresse</th>
             <th>Affectation</th>
+            <th>Depuis le</th>
             <th></th>
         </tr>
     {% endif %}
         <tr>
             {% with service=ip.allocation.service %}
             <td><a href="{% url 'ip-detail' ip.pk %}">{{ ip }}</a></td>
-            <td>{% if service %}<a href="{% url 'service-detail' service.pk %}">{{ service }}</a>{% else %}–{% endif %}</td>
+            {% if service %}
+            <td><a href="{% url 'service-detail' service.pk %}">{{ service }}</a></td>
+            <td>{{ ip.allocation.start }}</td>
+            {% else %}
+            <td>–</td>
+            <td>–</td>
+            {% endif %}
             <td class="text-right">
                 {% if service %}
                 <a href="#" data-toggle="modal" data-target="#confirm-deallocate" data-action="{% url 'deallocate' ip.allocation.pk %}" data-ip="{{ ip }}" data-service="{{ ip.allocation.service }}" class="btn btn-xs btn-danger">

+ 20 - 5
services/templates/services/resourceallocation_form.html

@@ -1,9 +1,24 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-
-<h1>{% if ipresource %}Modifier une allocation d’IP{% else %}Allouer une IP{% endif %}</h1>
+{% block breadcrumb %}
+{% if service %}
+    <li><a href="{% url 'service-list' %}">Services</a></li>
+    <li><a href="{{ service.get_absolute_url }}">{{ service }}</a></li>
+{% elif ipresource %}
+    <li><a href="{% url 'ip-list' %}">IP</a></li>
+    <li><a href="{{ ipresource.get_absolute_url }}">{{ ipresource }}</a></li>
+{% endif %}
+<li class="active">Allocation</li>
+{% endblock %}
 
-{% include '_form.html' %}
+{% block content %}
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        <h4>Allouer une IP</h4>
+    </div>
+    <div class="panel-body">
+        {% include '_form.html' %}
+    </div>
+</div>
 
 {% endblock %}

+ 6 - 4
services/templates/services/service_detail.html

@@ -1,9 +1,11 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
 {% load bootstrap3 %}
 
-{% block usertab %}{% if service.adhesion == request.user.profile.adhesion %} class="active"{% endif %}{% endblock %}
-{% block corptab %}{% if service.adhesion != request.user.profile.adhesion %} active{% endif %}{% endblock %}
+{% block breadcrumb %}
+<li><a href="{% url 'service-list' %}">Services</a></li>
+<li class="active">{{ service }}</li>
+{% endblock %}
 
 {% block content %}
 <div class="panel panel-{{ service.active|yesno:"success,danger"}}">
@@ -24,7 +26,7 @@
             <a href="{{ service.adhesion.get_adherent_detail_url }}">{{ service.adhesion.get_adherent_name }}</a>
         </p>
         {% if service.contribution %}
-        <p>Contribution : {{ service.contribution }}</p>
+        <p>Contribution : <a href="{{ service.contribution.get_absolute_url }}">{{ service.contribution }}</a></p>
         {% else %}
         <p>Contribution : pas de contribution</p>
         {% endif %}

+ 37 - 0
services/templates/services/service_detail_user.html

@@ -0,0 +1,37 @@
+{% extends 'base.html' %}
+
+{% load bootstrap3 %}
+
+{% block usertab %}{% if service.adhesion == request.user.profile.adhesion %} class="active"{% endif %}{% endblock %}
+{% block corptab %}{% if service.adhesion != request.user.profile.adhesion %} active{% endif %}{% endblock %}
+
+{% block content %}
+<div class="panel panel-{{ service.active|yesno:"success,danger"}}">
+    <div class="panel-heading">
+        <h3>{{ service.service_type }}</h3>
+        {% if service.label %}
+        <h4>{{ service.label }}</h4>
+        {% endif %}
+    </div>
+    <div class="panel-body">
+        <p>Identifiant du service : #{{ service.id }}</p>
+        <p>Responsable : {{ service.adhesion }}</p>
+        {% if service.contribution %}
+        <p>Contribution : {{ service.contribution }}</p>
+        {% else %}
+        <p>Contribution : pas de contribution</p>
+        {% endif %}
+        <p>Actif : {{ service.active|yesno:"oui,non" }}</p>
+        <p>
+            IP allouée{{ service.active_allocations.count|pluralize }} :
+            {% for allocation in service.active_allocations %}
+            {% if forloop.first %}<ul>{% endif %}
+                <li>{{ allocation.resource }} (depuis le {{ allocation.start }})</li>
+            {% if forloop.last %}</ul>{% endif %}
+            {% empty %}
+            aucune IP allouée
+            {% endfor %}
+        </p>
+    </div>
+</div>
+{% endblock %}

+ 18 - 5
services/templates/services/service_form.html

@@ -1,9 +1,22 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-
-<h1>{% if service %}Modifier{% else %}Ajouter{% endif %} un service</h1>
+{% block breadcrumb %}
+<li><a href="{% url 'service-list' %}">Services</a></li>
+{% if service %}
+<li><a href="{{ service.get_absolute_url }}">{{ service }}</a></li>
+<li class="active">Modification</li>
+{% else %}
+<li class="active">Nouveau</li>
+{% endif %}
+{% endblock %}
 
+{% block content %}
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        <h4>{% if service %}Modifier{% else %}Ajouter{% endif %} un service</h4>
+    </div>
+    <div class="panel-body">
 {% include '_form.html' %}
-
+    </div>
+</div>
 {% endblock %}

+ 6 - 2
services/templates/services/service_list.html

@@ -1,12 +1,16 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
 {% load bootstrap3 %}
 
+{% block breadcrumb %}
+<li class="active">Services</li>
+{% endblock %}
+
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
         <a class="btn btn-success pull-right" href="{% url 'service-add' %}">Ajouter un service</a>
-        <h1>Services</h1>
+        <h4>Services</h4>
     </div>
     {% for service in object_list %}
     {% if forloop.first %}

+ 6 - 4
services/templates/services/servicetype_detail.html

@@ -1,14 +1,16 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
 {% load bootstrap3 %}
 
-{% block usertab %}{% if service.adhesion == request.user.profile.adhesion %} class="active"{% endif %}{% endblock %}
-{% block corptab %}{% if service.adhesion != request.user.profile.adhesion %} active{% endif %}{% endblock %}
+{% block breadcrumb %}
+<li><a href="{% url 'servicetype-list' %}">Types de services</a></li>
+<li class="active">{{ servicetype }}</li>
+{% endblock %}
 
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
-        <h3>{{ servicetype }}</h3>
+        <h4>{{ servicetype }}</h4>
     </div>
     {% for service in servicetype.services.all %}
     {% if forloop.first %}

+ 18 - 5
services/templates/services/servicetype_form.html

@@ -1,9 +1,22 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
-{% block content %}
-
-<h1>{% if ipresource %}Modifier{% else %}Ajouter{% endif %} un type de service</h1>
+{% block breadcrumb %}
+<li><a href="{% url 'servicetype-list' %}">Types de services</a></li>
+{% if servicetype %}
+<li><a href="{{ servicetype.get_absolute_url }}">{{ servicetype }}</a></li>
+<li class="active">Modification</li>
+{% else %}
+<li class="active">Nouveau</li>
+{% endif %}
+{% endblock %}
 
+{% block content %}
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        <h4>{% if servicetype %}Modifier{% else %}Ajouter{% endif %} un type de service</h4>
+    </div>
+    <div class="panel-body">
 {% include '_form.html' %}
-
+    </div>
+</div>
 {% endblock %}

+ 6 - 2
services/templates/services/servicetype_list.html

@@ -1,12 +1,16 @@
-{% extends 'base.html' %}
+{% extends 'admin.html' %}
 
 {% load bootstrap3 %}
 
+{% block breadcrumb %}
+<li class="active">Types de services</li>
+{% endblock %}
+
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
         <a class="btn btn-success pull-right" href="{% url 'servicetype-add' %}">Ajouter un type de service</a>
-        <h1>Types de services</h1>
+        <h4>Types de services</h4>
     </div>
     {% for type in object_list %}
     {% if forloop.first %}

+ 1 - 1
services/tests.py

@@ -42,7 +42,7 @@ class ServicesMixin:
 class ViewsTestCase(ServicesMixin, TestCase):
     def test_home_service_list(self):
         self.client.login(username='user1', password='user1')
-        response = self.client.get(reverse('user'))
+        response = self.client.get(reverse('adhesion-detail-user'))
         self.assertContains(response, 'Service 1')
         self.assertNotContains(response, 'Service 2')
         self.assertNotContains(response, 'Service 3')

+ 16 - 13
services/urls.py

@@ -4,17 +4,20 @@ from . import views
 
 
 urlpatterns = [
-    url(r'^services/$', views.ServiceList.as_view(), name='service-list'),
-    url(r'^services/(?P<pk>[0-9]+)/$', views.ServiceDetail.as_view(), name='service-detail'),
-    url(r'^services/add/$', views.ServiceCreate.as_view(), name='service-add'),
-    url(r'^services/(?P<pk>[0-9]+)/edit/$', views.ServiceUpdate.as_view(), name='service-edit'),
-    url(r'^services/(?P<pk>[0-9]+)/allocate/$', views.ServiceAllocate.as_view(), name='service-allocate'),
-    url(r'^servicetypes/$', views.ServiceTypeList.as_view(), name='servicetype-list'),
-    url(r'^servicetypes/add/$', views.ServiceTypeCreate.as_view(), name='servicetype-add'),
-    url(r'^servicetypes/(?P<pk>[0-9]+)/$', views.ServiceTypeDetail.as_view(), name='servicetype-detail'),
-    url(r'^servicetypes/(?P<pk>[0-9]+)/edit/$', views.ServiceTypeUpdate.as_view(), name='servicetype-edit'),
-    url(r'^ip/$', views.IPResourceList.as_view(), name='ip-list'),
-    url(r'^ip/(?P<pk>[0-9]+)/$', views.IPResourceDetail.as_view(), name='ip-detail'),
-    url(r'^ip/(?P<pk>[0-9]+)/allocate/$', views.IPResourceAllocate.as_view(), name='ip-allocate'),
-    url(r'^allocations/(?P<pk>[0-9]+)/deallocate/$', views.ResourceAllocationDeallocate.as_view(), name='deallocate'),
+    # User views
+    url(r'^services/(?P<pk>[0-9]+)/$', views.ServiceDetail.as_view(template_name='services/service_detail_user.html'), name='service-detail-user'),
+    # Admin views
+    url(r'^admin/services/$', views.ServiceList.as_view(), name='service-list'),
+    url(r'^admin/services/(?P<pk>[0-9]+)/$', views.ServiceDetail.as_view(), name='service-detail'),
+    url(r'^admin/services/add/$', views.ServiceCreate.as_view(), name='service-add'),
+    url(r'^admin/services/(?P<pk>[0-9]+)/edit/$', views.ServiceUpdate.as_view(), name='service-edit'),
+    url(r'^admin/services/(?P<pk>[0-9]+)/allocate/$', views.ServiceAllocate.as_view(), name='service-allocate'),
+    url(r'^admin/servicetypes/$', views.ServiceTypeList.as_view(), name='servicetype-list'),
+    url(r'^admin/servicetypes/add/$', views.ServiceTypeCreate.as_view(), name='servicetype-add'),
+    url(r'^admin/servicetypes/(?P<pk>[0-9]+)/$', views.ServiceTypeDetail.as_view(), name='servicetype-detail'),
+    url(r'^admin/servicetypes/(?P<pk>[0-9]+)/edit/$', views.ServiceTypeUpdate.as_view(), name='servicetype-edit'),
+    url(r'^admin/ip/$', views.IPResourceList.as_view(), name='ip-list'),
+    url(r'^admin/ip/(?P<pk>[0-9]+)/$', views.IPResourceDetail.as_view(), name='ip-detail'),
+    url(r'^admin/ip/(?P<pk>[0-9]+)/allocate/$', views.IPResourceAllocate.as_view(), name='ip-allocate'),
+    url(r'^admin/allocations/(?P<pk>[0-9]+)/deallocate/$', views.ResourceAllocationDeallocate.as_view(), name='deallocate'),
 ]

+ 22 - 7
services/views.py

@@ -4,8 +4,7 @@ from django.views.generic.detail import SingleObjectMixin
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.mixins import PermissionRequiredMixin
 from django.http import HttpResponseGone
-from django.core.exceptions import PermissionDenied
-from django.utils import timezone
+from django.urls import reverse
 
 from djadhere.utils import get_active_filter
 from .models import Service, ServiceType, IPResource, ResourceAllocation
@@ -41,16 +40,25 @@ class ServiceUpdate(ServiceMixin, UpdateView):
 class ServiceAllocate(PermissionRequiredMixin, CreateView):
     model = ResourceAllocation
     permission_required = 'services.change_resourceallocation'
+    fields = ('service', 'resource',)
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['service'] = get_object_or_404(Service, pk=self.kwargs['pk'])
+        return context
 
     def get_form(self):
         service = get_object_or_404(Service, pk=self.kwargs['pk'])
         kwargs = super().get_form_kwargs()
         kwargs['initial'].update({'service': service.pk})
-        form = ResourceAllocationForm(**kwargs)
+        form = self.get_form_class()(**kwargs)
         form.fields['service'].disabled = True
         form.fields['resource'].queryset = IPResource.objects.exclude(get_active_filter('allocation'))
         return form
 
+    def get_success_url(self):
+        return reverse('service-detail', kwargs={'pk': self.kwargs['pk']})
+
 
 class ServiceTypeMixin(PermissionRequiredMixin):
     model = ServiceType
@@ -89,6 +97,12 @@ class IPResourceDetail(IPResourceMixin, DetailView):
 class IPResourceAllocate(PermissionRequiredMixin, CreateView):
     model = ResourceAllocation
     permission_required = 'services.change_resourceallocation'
+    fields = ('service', 'resource',)
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['ipresource'] = get_object_or_404(IPResource, pk=self.kwargs['pk'])
+        return context
 
     def get(self, request, *args, **kwargs):
         resource = get_object_or_404(IPResource, pk=self.kwargs['pk'])
@@ -100,10 +114,13 @@ class IPResourceAllocate(PermissionRequiredMixin, CreateView):
         resource = get_object_or_404(IPResource, pk=self.kwargs['pk'])
         kwargs = super().get_form_kwargs()
         kwargs['initial'].update({'resource': resource.pk})
-        form = ResourceAllocationForm(**kwargs)
+        form = self.get_form_class()(**kwargs)
         form.fields['resource'].disabled = True
         return form
 
+    def get_success_url(self):
+        return reverse('ip-detail', kwargs={'pk': self.kwargs['pk']})
+
 
 class ResourceAllocationMixin(PermissionRequiredMixin):
     model = ResourceAllocation
@@ -115,9 +132,7 @@ class ResourceAllocationDeallocate(ResourceAllocationMixin, SingleObjectMixin, R
 
     def get_object(self, queryset=None):
         obj = super().get_object(queryset)
-        if not obj.active:
-            raise PermissionDenied
-        obj.end = timezone.now()
+        obj.deallocate()
         obj.save()
         return obj