diff --git a/cotisations/models.py b/cotisations/models.py
index 2e71d418..ac75760b 100644
--- a/cotisations/models.py
+++ b/cotisations/models.py
@@ -96,6 +96,16 @@ class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
)
return name
+ def name_detailed(self):
+ """
+ Return:
+ - a list of strings with the name of all article in the invoice
+ and their quantity.
+ """
+ ventes = self.vente_set.all()
+ strings = ["{} x {}".format(v.number, v.name) for v in ventes]
+ return strings
+
# TODO : change facture to invoice
class Facture(BaseInvoice):
diff --git a/cotisations/templates/cotisations/aff_profil.html b/cotisations/templates/cotisations/aff_profil.html
new file mode 100644
index 00000000..a6e21624
--- /dev/null
+++ b/cotisations/templates/cotisations/aff_profil.html
@@ -0,0 +1,141 @@
+{% 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 Lara 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 %}
+
+
+
+
+
+
+ {% trans "Subscriptions" %}
+
+
+
+
+ {% can_create Facture %}
+
+
+ {% trans "Add a subscription" %}
+
+ {% if user_solde %}
+
+
+ {% trans "Edit the balance" %}
+
+ {% endif%}
+ {% acl_end %}
+
+
+ {% if facture_list %}
+
+ {% if facture_list.paginator %}
+ {% include 'pagination.html' with list=facture_list %}
+ {% endif %}
+
+
+
+
+
+ {% trans "User" as tr_user %}
+ {% include 'buttons/sort.html' with prefix='cotis' col='user' text=tr_user %}
+ |
+ {% trans "Designation" %} |
+ {% trans "Total price" %} |
+
+ {% trans "Payment method" as tr_payment_method %}
+ {% include 'buttons/sort.html' with prefix='cotis' col='paiement' text=tr_payment_method %}
+ |
+
+ {% trans "Date" as tr_date %}
+ {% include 'buttons/sort.html' with prefix='cotis' col='date' text=tr_date %}
+ |
+
+ {% trans "Invoice ID" as tr_invoice_id %}
+ {% include 'buttons/sort.html' with prefix='cotis' col='id' text=tr_invoice_id %}
+ |
+ |
+ |
+
+
+ {% for facture in facture_list %}
+
+ {{ facture.user }} |
+
+
+ {% for article in facture.name_detailed %}
+
+
+ {{ article }}
+ |
+
+ {% endfor %}
+
+ |
+ {{ facture.prix_total }} |
+ {{ facture.paiement }} |
+ {{ facture.date }} |
+ {{ facture.id }} |
+
+ {% can_edit facture %}
+ {% include 'buttons/edit.html' with href='cotisations:edit-facture' id=facture.id %}
+ {% acl_else %}
+ {% trans "Controlled invoice" %}
+ {% acl_end %}
+ {% can_delete facture %}
+ {% include 'buttons/suppr.html' with href='cotisations:del-facture' id=facture.id %}
+ {% acl_end %}
+ {% history_button facture %}
+ |
+
+ {% if facture.valid %}
+
+ {% trans "PDF" %}
+
+ {% else %}
+ {% trans "Invalidated invoice" %}
+ {% endif %}
+ {% if facture.control and facture.is_subscription %}
+
+ {% trans "Voucher" %}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+ {% if facture_list.paginator %}
+ {% include 'pagination.html' with list=facture_list %}
+ {% endif %}
+
+ {% else %}
+
{% trans "No invoice" %}
+ {% endif %}
+
+
+
diff --git a/cotisations/views.py b/cotisations/views.py
index b90b39e4..800c9df8 100644
--- a/cotisations/views.py
+++ b/cotisations/views.py
@@ -33,6 +33,7 @@ import os
from django.urls import reverse
from django.shortcuts import render, redirect, get_object_or_404
+from django.template.loader import render_to_string
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db.models import ProtectedError
@@ -1060,3 +1061,26 @@ def voucher_pdf(request, invoice, **_kwargs):
"date_begin": invoice.date,
},
)
+
+def aff_profil(request,user):
+ """View used to display the cotisations on a user's profil."""
+
+ factures = Facture.objects.filter(user=user)
+ factures = SortTable.sort(
+ factures,
+ request.GET.get("col"),
+ request.GET.get("order"),
+ SortTable.COTISATIONS_INDEX,
+ )
+
+ pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
+ factures = re2o_paginator(request, factures,pagination_large_number)
+
+ context = {
+ "users":user,
+ "facture_list": factures,
+ }
+
+ return render_to_string(
+ "cotisations/aff_profil.html",context=context,request=request,using=None
+ )
diff --git a/machines/templates/machines/aff_profil.html b/machines/templates/machines/aff_profil.html
new file mode 100644
index 00000000..4b2650ed
--- /dev/null
+++ b/machines/templates/machines/aff_profil.html
@@ -0,0 +1,271 @@
+{% 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 Lara 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 logs_extra %}
+{% load i18n %}
+
+
+
+
+
+ {% trans "Machines" %}
+ {{ nb_machines }}
+
+
+
+
+
+ {% if machines_list %}
+
+ {% if machines_list.paginator %}
+ {% include 'pagination.html' with list=machines_list go_to_id="machines" %}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ {% trans "DNS name" as tr_dns_name %}
+ {% include 'buttons/sort.html' with prefix='machine' col='name' text=tr_dns_name %} |
+ {% trans "Type" %} |
+ {% trans "MAC address" %} |
+ {% trans "IP address" %} |
+ {% trans "Actions" %} |
+
+ {% for machine in machines_list %}
+
+
+ {% trans "No name" as tr_no_name %}
+ {% trans "View the profile" as tr_view_the_profile %}
+ {% if machine.active %}
+
+ {% else %}
+ {% trans "Deactivated" %}:
+ {% endif %}
+ {{ machine.get_name|default:tr_no_name }}
+
+ {{ machine.user }}
+
+ |
+
+ {% can_create Interface machine.id %}
+ {% 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 %}
+ {% history_button machine %}
+ {% can_delete machine %}
+ {% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %}
+ {% acl_end %}
+ |
+
+ {% for interface in machine.interface_set.all %}
+
+
+ {% if interface.domain.related_domain.all %}
+ {{ interface.domain }}
+
+ {% else %}
+ {{ interface.domain }}
+ {% endif %}
+ |
+
+ {{ interface.machine_type }}
+ |
+
+ {{ interface.mac_address }}
+
+ |
+
+ IPv4 {{ interface.ipv4 }}
+
+ {% if ipv6_enabled and interface.ipv6 != 'None' %}
+ IPv6
+
+ {% endif %}
+ |
+
+
+
+
+
+
+ {% history_button interface %}
+ {% can_delete interface %}
+ {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %}
+ {% acl_end %}
+
+ |
+
+
+
+
+
+ -
+ {{ interface.get_vendor }}
+
+
+
+ |
+
+ {% if ipv6_enabled and interface.ipv6 != 'None' %}
+
+
+
+
+ {% for ipv6 in interface.ipv6.all %}
+ -
+ {{ ipv6 }}
+
+ {% endfor %}
+
+
+ |
+
+ {% endif %}
+ {% if interface.domain.related_domain.all %}
+
+
+
+
+ {% for al in interface.domain.related_domain.all %}
+ -
+
+ {{ al }}
+
+
+
+ {% endfor %}
+
+
+ |
+
+ {% endif %}
+ {% endfor %}
+
+ |
+
+ {% endfor %}
+
+
+
+
+
+ {% if machines_list.paginator %}
+ {% include 'pagination.html' with list=machines_list go_to_id="machines" %}
+ {% endif %}
+
+ {% else %}
+
{% trans "No machine" %}
+ {% endif %}
+
+
+
diff --git a/machines/views.py b/machines/views.py
index 15f59e00..76d12eca 100644
--- a/machines/views.py
+++ b/machines/views.py
@@ -39,6 +39,7 @@ from django.db import IntegrityError
from django.forms import modelformset_factory
from django.http import HttpResponse
from django.shortcuts import render, redirect
+from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
@@ -1311,6 +1312,40 @@ def index(request):
machines_list = re2o_paginator(request, machines_list, pagination_large_number)
return render(request, "machines/index.html", {"machines_list": machines_list})
+# Canonic view for displaying machines in users's profil
+def aff_profil(request, user):
+ """View used to display the machines on a user's profile."""
+ machines = (
+ Machine.objects.filter(user=user)
+ .select_related("user")
+ .prefetch_related("interface_set__domain__extension")
+ .prefetch_related("interface_set__ipv4__ip_type__extension")
+ .prefetch_related("interface_set__machine_type")
+ .prefetch_related("interface_set__domain__related_domain__extension")
+ )
+ machines = SortTable.sort(
+ machines,
+ request.GET.get("col"),
+ request.GET.get("order"),
+ SortTable.MACHINES_INDEX,
+ )
+ nb_machines = machines.count()
+ pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
+ machines = re2o_paginator(request, machines, pagination_large_number)
+
+ context = {
+ "users":user,
+ "machines_list": machines,
+ "nb_machines":nb_machines,
+ }
+
+ return render_to_string(
+ "machines/aff_profil.html",context=context,request=request,using=None
+ )
+
+
+
+
@login_required
@can_view_all(IpType)
diff --git a/re2o/context_processors.py b/re2o/context_processors.py
index b6c54e22..6b61e2a0 100644
--- a/re2o/context_processors.py
+++ b/re2o/context_processors.py
@@ -26,6 +26,7 @@ from __future__ import unicode_literals
import datetime
from django.contrib import messages
+from django.contrib.messages import get_messages
from django.http import HttpRequest
from preferences.models import GeneralOption, OptionalMachine
from django.utils.translation import get_language
@@ -47,9 +48,11 @@ def context_user(request):
global_message = GeneralOption.get_cached_value("general_message_en")
if global_message:
if isinstance(request, HttpRequest):
- messages.warning(request, global_message)
+ if global_message not in [msg.message for msg in get_messages(request)]:
+ messages.warning(request, global_message)
else:
- messages.warning(request._request, global_message)
+ if global_message not in [msg.message for msg in get_messages(request._request)]:
+ messages.warning(request._request, global_message)
if user.is_authenticated():
interfaces = user.user_interfaces()
else:
diff --git a/tickets/templates/tickets/profil.html b/tickets/templates/tickets/aff_profil.html
similarity index 100%
rename from tickets/templates/tickets/profil.html
rename to tickets/templates/tickets/aff_profil.html
diff --git a/tickets/views.py b/tickets/views.py
index c1796f8d..a87285ae 100644
--- a/tickets/views.py
+++ b/tickets/views.py
@@ -193,7 +193,7 @@ def aff_tickets(request):
# Canonic views for optional apps
-def profil(request, user):
+def aff_profil(request, user):
"""View used to display the tickets on a user's profile."""
tickets_list = Ticket.objects.filter(user=user).all().order_by("-date")
nbr_tickets = tickets_list.count()
@@ -214,7 +214,7 @@ def profil(request, user):
"nbr_tickets_unsolved": nbr_tickets_unsolved,
}
return render_to_string(
- "tickets/profil.html", context=context, request=request, using=None
+ "tickets/aff_profil.html", context=context, request=request, using=None
)
diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html
index ad0605c7..a676b416 100644
--- a/users/templates/users/profil.html
+++ b/users/templates/users/profil.html
@@ -407,63 +407,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
-
-
-
-
- {% trans "Machines" %}
- {{ nb_machines }}
-
-
-
-
-
- {% if machines_list %}
- {% include 'machines/aff_machines.html' with machines_list=machines_list %}
- {% else %}
-
{% trans "No machine" %}
- {% endif %}
-
-
-
-
-
-
-
- {% trans "Subscriptions" %}
-
-
-
-
+
+
+ {% for template in apps_templates_list %}
+ {{ template }}
+ {% endfor %}
+
@@ -569,9 +518,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {% for template in optionnal_templates_list %}
- {{ template }}
- {% endfor %}
{% endblock %}
diff --git a/users/views.py b/users/views.py
index 54f67b77..ffa1344e 100644
--- a/users/views.py
+++ b/users/views.py
@@ -77,7 +77,7 @@ from machines.models import Machine
from preferences.models import OptionalUser, GeneralOption, AssoOption
from importlib import import_module
from django.conf import settings
-from re2o.settings_local import OPTIONNAL_APPS_RE2O
+from re2o.settings import LOCAL_APPS, OPTIONNAL_APPS_RE2O
from re2o.views import form
from re2o.utils import all_has_access, permission_tree
from re2o.base import re2o_paginator, SortTable
@@ -1314,8 +1314,8 @@ def index_serviceusers(request):
def mon_profil(request):
"""Shortcuts view to profil view, with correct arguments.
Returns the view profil with users argument, users is set to
- default request.user.
-
+ default request.user.
+
Parameters:
request (django request): Standard django request.
@@ -1346,40 +1346,18 @@ def profil(request, users, **_kwargs):
Returns:
Django User Profil Form.
-
"""
- machines = (
- Machine.objects.filter(user=users)
- .select_related("user")
- .prefetch_related("interface_set__domain__extension")
- .prefetch_related("interface_set__ipv4__ip_type__extension")
- .prefetch_related("interface_set__machine_type")
- .prefetch_related("interface_set__domain__related_domain__extension")
- )
- machines = SortTable.sort(
- machines,
- request.GET.get("col"),
- request.GET.get("order"),
- SortTable.MACHINES_INDEX,
- )
- optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
- optionnal_templates_list = [
- app.views.profil(request, users)
- for app in optionnal_apps
- if hasattr(app.views, "profil")
+ # Generate the template list for all apps of re2o if relevant
+ apps = [import_module(app) for app in LOCAL_APPS + OPTIONNAL_APPS_RE2O]
+ apps_templates_list = [
+ app.views.aff_profil(request, users)
+ for app in apps
+ if hasattr(app.views, "aff_profil")
]
- pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
- nb_machines = machines.count()
- machines = re2o_paginator(request, machines, pagination_large_number)
- factures = Facture.objects.filter(user=users)
- factures = SortTable.sort(
- factures,
- request.GET.get("col"),
- request.GET.get("order"),
- SortTable.COTISATIONS_INDEX,
- )
+ nb_machines = users.user_interfaces().count()
+
bans = Ban.objects.filter(user=users)
bans = SortTable.sort(
bans,
@@ -1405,10 +1383,8 @@ def profil(request, users, **_kwargs):
"users/profil.html",
{
"users": users,
- "machines_list": machines,
- "nb_machines": nb_machines,
- "optionnal_templates_list": optionnal_templates_list,
- "facture_list": factures,
+ "nb_machines":nb_machines,
+ "apps_templates_list": apps_templates_list,
"ban_list": bans,
"white_list": whitelists,
"user_solde": user_solde,