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 %} + + + + + + + + + + + + + + + {% for facture in facture_list %} + + + + + + + + + + + {% endfor %} +
+ {% 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 %} +
{{ facture.user }} + + {% for article in facture.name_detailed %} + + + + {% endfor %} +
+ {{ article }} +
+
{{ 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 %} +
+ + {% 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 %} + + + + + + + {% for machine in machines_list %} + + + + + {% for interface in machine.interface_set.all %} + + + + + + + + + + + {% if ipv6_enabled and interface.ipv6 != 'None' %} + + + + {% endif %} + {% if interface.domain.related_domain.all %} + + + + {% endif %} + {% endfor %} + + + + {% endfor %} + +
{% include 'buttons/sort.html' with prefix='machine' col='name' text=tr_dns_name %}{% trans "Type" %}{% trans "MAC address" %}{% trans "IP address" %}{% trans "Actions" %}
+ {% 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 %} +
+ {% 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 }} +
  • +
+
+
+
+
    + {% for ipv6 in interface.ipv6.all %} +
  • + {{ ipv6 }} +
  • + {% endfor %} +
+
+
+
+
    + {% for al in interface.domain.related_domain.all %} +
  • + + {{ al }} + + +
  • + {% 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" %} -

-
-
-
- {% can_create Facture %} - - - {% trans "Add a subscription" %} - - {% if user_solde %} - - - {% trans "Edit the balance" %} - - {% endif%} - {% acl_end %} -
-
- {% if facture_list %} - {% include 'cotisations/aff_cotisations.html' with facture_list=facture_list %} - {% else %} -

{% trans "No invoice" %}

- {% endif %} -
-
-
+ + + {% 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,