diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index b36e212..90fe9a2 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-23 21:43+0100\n" +"POT-Creation-Date: 2024-02-23 22:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -31,7 +31,7 @@ msgstr "équipes" #: participation/admin.py:79 participation/admin.py:140 #: participation/admin.py:171 participation/models.py:419 #: participation/models.py:443 participation/models.py:513 -#: registration/models.py:601 +#: registration/models.py:619 #: registration/templates/registration/payment_form.html:51 msgid "tournament" msgstr "tournoi" @@ -333,7 +333,8 @@ msgstr "Continuer le tirage" #: draw/templates/draw/tournament_content.html:216 participation/admin.py:167 #: participation/models.py:249 participation/models.py:434 -#: registration/models.py:157 registration/models.py:592 +#: registration/models.py:157 registration/models.py:610 +#: registration/tables.py:39 #: registration/templates/registration/payment_form.html:50 msgid "team" msgstr "équipe" @@ -376,8 +377,8 @@ msgstr "Êtes-vous sûr·e de vouloir annuler le tirage au sort ?" msgid "Close" msgstr "Fermer" -#: draw/views.py:31 participation/views.py:151 participation/views.py:444 -#: participation/views.py:475 +#: draw/views.py:31 participation/views.py:154 participation/views.py:447 +#: participation/views.py:478 msgid "You are not in a team." msgstr "Vous n'êtes pas dans une équipe." @@ -529,7 +530,7 @@ msgstr "Ajouter un⋅e nouvelleau juré⋅e" #: participation/forms.py:227 #: participation/templates/participation/pool_detail.html:123 -#: participation/templates/participation/tournament_detail.html:111 +#: participation/templates/participation/tournament_detail.html:119 msgid "Add" msgstr "Ajouter" @@ -1077,12 +1078,12 @@ msgstr "" #: participation/templates/participation/create_team.html:11 #: participation/templates/participation/tournament_form.html:14 -#: tfjm/templates/base.html:83 +#: tfjm/templates/base.html:80 msgid "Create" msgstr "Créer" #: participation/templates/participation/join_team.html:11 -#: tfjm/templates/base.html:78 +#: tfjm/templates/base.html:75 msgid "Join" msgstr "Rejoindre" @@ -1098,6 +1099,7 @@ msgstr "Rejoindre" #: participation/templates/participation/team_detail.html:215 #: participation/templates/participation/tournament_form.html:12 #: participation/templates/participation/update_team.html:12 +#: registration/tables.py:46 #: registration/templates/registration/payment_form.html:207 #: registration/templates/registration/update_user.html:16 #: registration/templates/registration/user_detail.html:186 @@ -1344,7 +1346,7 @@ msgid "BigBlueButton link:" msgstr "Lien BigBlueButton :" #: participation/templates/participation/pool_detail.html:71 -#: participation/templates/participation/tournament_detail.html:97 +#: participation/templates/participation/tournament_detail.html:105 msgid "Ranking" msgstr "Classement" @@ -1458,7 +1460,7 @@ msgid "Payment of" msgstr "Paiement de" #: participation/templates/participation/team_detail.html:132 -#: registration/models.py:522 +#: registration/models.py:540 msgid "grouped" msgstr "groupé" @@ -1523,7 +1525,7 @@ msgid "Invalidate" msgstr "Invalider" #: participation/templates/participation/team_detail.html:209 -#: participation/views.py:329 +#: participation/views.py:332 msgid "Upload motivation letter" msgstr "Envoyer la lettre de motivation" @@ -1532,7 +1534,7 @@ msgid "Update team" msgstr "Modifier l'équipe" #: participation/templates/participation/team_detail.html:219 -#: participation/views.py:438 +#: participation/views.py:441 msgid "Leave team" msgstr "Quitter l'équipe" @@ -1541,7 +1543,6 @@ msgid "Are you sure that you want to leave this team?" msgstr "Êtes-vous sûr·e de vouloir quitter cette équipe ?" #: participation/templates/participation/team_list.html:6 -#: tfjm/templates/base.html:71 msgid "All teams" msgstr "Toutes les équipes" @@ -1616,14 +1617,18 @@ msgid "Teams" msgstr "Équipes" #: participation/templates/participation/tournament_detail.html:80 +msgid "Access to payments list" +msgstr "Accéder à la liste des paiements" + +#: participation/templates/participation/tournament_detail.html:88 msgid "Pools" msgstr "Poules" -#: participation/templates/participation/tournament_detail.html:88 +#: participation/templates/participation/tournament_detail.html:96 msgid "Add new pool" msgstr "Ajouter une nouvelle poule" -#: participation/templates/participation/tournament_detail.html:110 +#: participation/templates/participation/tournament_detail.html:118 msgid "Add pool" msgstr "Ajouter une poule" @@ -1636,6 +1641,10 @@ msgstr "Tous les tournois" msgid "Add tournament" msgstr "Ajouter un tournoi" +#: participation/templates/participation/tournament_payments.html:9 +msgid "Back to tournament page" +msgstr "Retour à la page du tournoi" + #: participation/templates/participation/upload_motivation_letter.html:6 msgid "Back to the team detail" msgstr "Retour aux détails de l'utilisateur⋅rice" @@ -1652,44 +1661,44 @@ msgstr "Modèles :" msgid "Warning: non-free format" msgstr "Attention : format non libre" -#: participation/views.py:51 tfjm/templates/base.html:82 +#: participation/views.py:54 tfjm/templates/base.html:79 #: tfjm/templates/navbar.html:35 msgid "Create team" msgstr "Créer une équipe" -#: participation/views.py:60 participation/views.py:101 +#: participation/views.py:63 participation/views.py:104 msgid "You don't participate, so you can't create a team." msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe." -#: participation/views.py:62 participation/views.py:103 +#: participation/views.py:65 participation/views.py:106 msgid "You are already in a team." msgstr "Vous êtes déjà dans une équipe." -#: participation/views.py:92 tfjm/templates/base.html:77 +#: participation/views.py:95 tfjm/templates/base.html:74 #: tfjm/templates/navbar.html:40 msgid "Join team" msgstr "Rejoindre une équipe" -#: participation/views.py:152 participation/views.py:476 +#: participation/views.py:155 participation/views.py:479 msgid "You don't participate, so you don't have any team." msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe." -#: participation/views.py:178 +#: participation/views.py:181 #, python-brace-format msgid "Detail of team {trigram}" msgstr "Détails de l'équipe {trigram}" -#: participation/views.py:207 +#: participation/views.py:210 msgid "You don't participate, so you can't request the validation of the team." msgstr "" "Vous ne participez pas, vous ne pouvez pas demander la validation de " "l'équipe." -#: participation/views.py:210 +#: participation/views.py:213 msgid "The validation of the team is already done or pending." msgstr "La validation de l'équipe est déjà faite ou en cours." -#: participation/views.py:213 +#: participation/views.py:216 msgid "" "The team can't be validated: missing email address confirmations, " "authorizations, people, motivation letter or the tournament is not set." @@ -1698,103 +1707,108 @@ msgstr "" "d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre " "de motivation, soit le tournoi n'a pas été choisi." -#: participation/views.py:235 +#: participation/views.py:238 msgid "You are not an organizer of the tournament." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi." -#: participation/views.py:238 +#: participation/views.py:241 msgid "This team has no pending validation." msgstr "L'équipe n'a pas de validation en attente." -#: participation/views.py:272 +#: participation/views.py:275 msgid "You must specify if you validate the registration or not." msgstr "Vous devez spécifier si vous validez l'inscription ou non." -#: participation/views.py:307 +#: participation/views.py:310 #, python-brace-format msgid "Update team {trigram}" msgstr "Mise à jour de l'équipe {trigram}" -#: participation/views.py:368 participation/views.py:424 +#: participation/views.py:371 participation/views.py:427 #, python-brace-format msgid "Motivation letter of {team}.{ext}" msgstr "Lettre de motivation de {team}.{ext}" -#: participation/views.py:399 +#: participation/views.py:402 #, python-brace-format msgid "Photo authorization of {participant}.{ext}" msgstr "Autorisation de droit à l'image de {participant}.{ext}" -#: participation/views.py:405 +#: participation/views.py:408 #, python-brace-format msgid "Parental authorization of {participant}.{ext}" msgstr "Autorisation parentale de {participant}.{ext}" -#: participation/views.py:412 +#: participation/views.py:415 #, python-brace-format msgid "Health sheet of {participant}.{ext}" msgstr "Fiche sanitaire de {participant}.{ext}" -#: participation/views.py:418 +#: participation/views.py:421 #, python-brace-format msgid "Vaccine sheet of {participant}.{ext}" msgstr "Carnet de vaccination de {participant}.{ext}" -#: participation/views.py:428 +#: participation/views.py:431 #, python-brace-format msgid "Photo authorizations of team {trigram}.zip" msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip" -#: participation/views.py:446 +#: participation/views.py:449 msgid "The team is already validated or the validation is pending." msgstr "La validation de l'équipe est déjà faite ou en cours." -#: participation/views.py:490 +#: participation/views.py:493 msgid "The team is not validated yet." msgstr "L'équipe n'est pas encore validée." -#: participation/views.py:504 +#: participation/views.py:507 #, python-brace-format msgid "Participation of team {trigram}" msgstr "Participation de l'équipe {trigram}" -#: participation/views.py:640 +#: participation/views.py:588 +#, python-brace-format +msgid "Payments of {tournament}" +msgstr "Paiements de {tournament}" + +#: participation/views.py:668 msgid "You can't upload a solution after the deadline." msgstr "Vous ne pouvez pas envoyer de solution après la date limite." -#: participation/views.py:748 +#: participation/views.py:776 #, python-brace-format msgid "Solutions for pool {pool} of tournament {tournament}.zip" msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip" -#: participation/views.py:749 +#: participation/views.py:777 #, python-brace-format msgid "Syntheses for pool {pool} of tournament {tournament}.zip" msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip" -#: participation/views.py:767 +#: participation/views.py:795 #, python-brace-format msgid "Jurys of {pool}" msgstr "Juré⋅es de la {pool}" -#: participation/views.py:794 +#: participation/views.py:822 msgid "New TFJM² jury account" msgstr "Nouveau compte de juré⋅e pour le TFJM²" -#: participation/views.py:807 +#: participation/views.py:835 #, python-brace-format msgid "The jury {name} has been successfully added!" msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !" -#: participation/views.py:844 +#: participation/views.py:872 msgid "The following user is not registered as a jury:" msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :" -#: participation/views.py:858 +#: participation/views.py:886 msgid "Notes were successfully uploaded." msgstr "Les notes ont bien été envoyées." -#: participation/views.py:1522 +#: participation/views.py:1550 msgid "You can't upload a synthesis after the deadline." msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite." @@ -1805,7 +1819,7 @@ msgstr "prénom" #: registration/admin.py:57 registration/admin.py:73 registration/admin.py:89 #: registration/admin.py:105 registration/admin.py:121 -#: registration/tables.py:17 +#: registration/tables.py:18 msgid "last name" msgstr "nom de famille" @@ -1873,7 +1887,7 @@ msgstr "" msgid "registration" msgstr "inscription" -#: registration/models.py:130 registration/models.py:518 +#: registration/models.py:130 registration/models.py:536 msgid "registrations" msgstr "inscriptions" @@ -2165,15 +2179,30 @@ msgstr "" "Vous pouvez vérifier le statut de l'équipe sur la page de " "l'équipe." -#: registration/models.py:502 +#: registration/models.py:504 +#, python-brace-format +msgid "" +"There are {valid} validated payments, {pending} pending and {invalid} " +"invalid for the tournament of {tournament}. You can check the status of the " +"payments on the payments list." +msgstr "" +"Il y a {valid} paiements validés, {pending} en attente et {invalid} " +"invalides pour le tournoi {tournament}. Vous pouvez vérifier le statut des " +"paiements sur la liste des paiements." + +#: registration/models.py:511 +msgid "Payments" +msgstr "Paiements" + +#: registration/models.py:520 msgid "volunteer registration" msgstr "inscription de bénévole" -#: registration/models.py:503 +#: registration/models.py:521 msgid "volunteer registrations" msgstr "inscriptions de bénévoles" -#: registration/models.py:524 +#: registration/models.py:542 msgid "" "If set to true, then one payment is made for the full team, for example if " "the school pays for all." @@ -2181,103 +2210,107 @@ msgstr "" "Si vrai, alors un seul paiement est fait pour toute l'équipe, par exemple si " "le lycée paie pour tout le monde." -#: registration/models.py:529 +#: registration/models.py:547 msgid "total amount" msgstr "montant total" -#: registration/models.py:530 +#: registration/models.py:548 msgid "Corresponds to the total required amount to pay, in euros." msgstr "Correspond au montant total à payer, en euros." -#: registration/models.py:535 +#: registration/models.py:553 msgid "token" msgstr "jeton" -#: registration/models.py:538 +#: registration/models.py:556 msgid "A token to authorize external users to make this payment." msgstr "Un jeton pour autoriser des utilisateurs externes à faire ce paiement." -#: registration/models.py:542 +#: registration/models.py:560 msgid "for final tournament" msgstr "pour la finale" -#: registration/models.py:547 +#: registration/models.py:565 msgid "type" msgstr "type" -#: registration/models.py:550 +#: registration/models.py:568 msgid "No payment" msgstr "Pas de paiement" -#: registration/models.py:551 +#: registration/models.py:569 #: registration/templates/registration/payment_form.html:70 msgid "Credit card" msgstr "Carte bancaire" -#: registration/models.py:552 +#: registration/models.py:570 msgid "Scholarship" msgstr "Notification de bourse" -#: registration/models.py:553 +#: registration/models.py:571 #: registration/templates/registration/payment_form.html:75 msgid "Bank transfer" msgstr "Virement bancaire" -#: registration/models.py:554 +#: registration/models.py:572 msgid "Other (please indicate)" msgstr "Autre (veuillez spécifier)" -#: registration/models.py:555 +#: registration/models.py:573 msgid "The tournament is free" msgstr "Le tournoi est gratuit" -#: registration/models.py:562 +#: registration/models.py:580 msgid "Hello Asso checkout intent ID" msgstr "ID de l'intention de paiement Hello Asso" -#: registration/models.py:569 +#: registration/models.py:587 msgid "receipt" msgstr "justificatif" -#: registration/models.py:570 +#: registration/models.py:588 msgid "only if you have a scholarship or if you chose a bank transfer." msgstr "" "Nécessaire seulement si vous déclarez être boursièr⋅e ou si vous payez par " "virement bancaire." -#: registration/models.py:577 +#: registration/models.py:595 msgid "additional information" msgstr "informations additionnelles" -#: registration/models.py:578 +#: registration/models.py:596 msgid "To help us to find your payment." msgstr "Pour nous aider à retrouver votre paiement, si nécessaire." -#: registration/models.py:584 +#: registration/models.py:602 msgid "payment valid" msgstr "paiement valide" -#: registration/models.py:644 +#: registration/models.py:662 msgid "Reminder for your payment" msgstr "Rappel pour votre paiement" -#: registration/models.py:655 +#: registration/models.py:673 msgid "Payment confirmation" msgstr "Confirmation de paiement" -#: registration/models.py:677 +#: registration/models.py:695 #, python-brace-format msgid "Payment of {registrations}" msgstr "Paiements de {registrations}" -#: registration/models.py:680 +#: registration/models.py:698 msgid "payment" msgstr "paiement" -#: registration/models.py:681 +#: registration/models.py:699 msgid "payments" msgstr "paiements" +#: registration/tables.py:69 +msgid "No payment yet." +msgstr "Pas encore de paiement." + #: registration/templates/registration/add_organizer.html:5 #: registration/templates/registration/add_organizer.html:12 #: registration/templates/registration/add_organizer.html:21 @@ -2522,7 +2555,7 @@ msgid "Your password has been set. You may go ahead and log in now." msgstr "Votre mot de passe a été changé. Vous pouvez désormais vous connecter." #: registration/templates/registration/password_reset_complete.html:10 -#: tfjm/templates/base.html:87 tfjm/templates/base.html:88 +#: tfjm/templates/base.html:84 tfjm/templates/base.html:85 #: tfjm/templates/navbar.html:98 tfjm/templates/registration/login.html:7 #: tfjm/templates/registration/login.html:8 #: tfjm/templates/registration/login.html:30 @@ -3067,7 +3100,7 @@ msgstr "" "avec les détails de l'erreur. Vous pouvez désormais retourner chercher " "d'autres solutions.." -#: tfjm/templates/base.html:74 +#: tfjm/templates/base.html:71 msgid "Search results" msgstr "Résultats de la recherche" diff --git a/participation/templates/participation/tournament_detail.html b/participation/templates/participation/tournament_detail.html index fdba961..0a9b075 100644 --- a/participation/templates/participation/tournament_detail.html +++ b/participation/templates/participation/tournament_detail.html @@ -73,6 +73,14 @@
{% render_table teams %}
+ + {% if user.registration.is_admin or user.registration in tournament.organizers.all %} + + {% endif %} {% if pools.data %}
diff --git a/participation/templates/participation/tournament_payments.html b/participation/templates/participation/tournament_payments.html new file mode 100644 index 0000000..96428aa --- /dev/null +++ b/participation/templates/participation/tournament_payments.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% load django_tables2 i18n %} + +{% block content %} + {% render_table table %} + + + {% trans "Back to tournament page" %} + +{% endblock %} diff --git a/participation/urls.py b/participation/urls.py index 3881148..e344c2c 100644 --- a/participation/urls.py +++ b/participation/urls.py @@ -10,7 +10,7 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, SolutionUploadView, \ SynthesisUploadView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \ TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \ - TournamentListView, TournamentUpdateView + TournamentListView, TournamentPaymentsView, TournamentUpdateView app_name = "participation" @@ -33,6 +33,7 @@ urlpatterns = [ path("tournament/create/", TournamentCreateView.as_view(), name="tournament_create"), path("tournament//", TournamentDetailView.as_view(), name="tournament_detail"), path("tournament//update/", TournamentUpdateView.as_view(), name="tournament_update"), + path("tournament//payments/", TournamentPaymentsView.as_view(), name="tournament_payments"), path("tournament//csv/", TournamentExportCSVView.as_view(), name="tournament_csv"), path("pools/create/", PoolCreateView.as_view(), name="pool_create"), path("pools//", PoolDetailView.as_view(), name="pool_detail"), diff --git a/participation/views.py b/participation/views.py index 5b2735d..832dc34 100644 --- a/participation/views.py +++ b/participation/views.py @@ -6,6 +6,7 @@ from io import BytesIO import os import subprocess from tempfile import mkdtemp +from typing import Any, Dict from zipfile import ZipFile from django.conf import settings @@ -15,6 +16,7 @@ from django.contrib.sites.models import Site from django.core.exceptions import PermissionDenied from django.core.mail import send_mail from django.db import transaction +from django.db.models import F from django.http import FileResponse, Http404, HttpResponse from django.shortcuts import redirect from django.template.loader import render_to_string @@ -24,13 +26,14 @@ from django.utils.crypto import get_random_string from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, FormView, RedirectView, TemplateView, UpdateView, View from django.views.generic.edit import FormMixin, ProcessFormView -from django_tables2 import MultiTableMixin, SingleTableView +from django_tables2 import MultiTableMixin, SingleTableMixin, SingleTableView from magic import Magic from odf.opendocument import OpenDocumentSpreadsheet from odf.style import Style, TableCellProperties, TableColumnProperties, TextProperties from odf.table import CoveredTableCell, Table, TableCell, TableColumn, TableRow from odf.text import P from registration.models import Payment, StudentRegistration, VolunteerRegistration +from registration.tables import PaymentTable from tfjm.lists import get_sympa_client from tfjm.views import AdminMixin, VolunteerMixin @@ -572,6 +575,31 @@ class TournamentDetailView(MultiTableMixin, DetailView): return context +class TournamentPaymentsView(VolunteerMixin, SingleTableMixin, DetailView): + """ + Display the list of payments of a tournament. + """ + model = Tournament + table_class = PaymentTable + template_name = "participation/tournament_payments.html" + + def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: + context = super().get_context_data(**kwargs) + context["title"] = _("Payments of {tournament}").format(tournament=self.object) + return context + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated or not self.request.user.registration.is_admin \ + and not (self.request.user.registration.is_volunteer + and self.get_object() in self.request.user.registration.organized_tournaments.all()): + return self.handle_no_permission() + return super().dispatch(request, *args, **kwargs) + + def get_table_data(self): + return Payment.objects.filter(registrations__team__participation__tournament=self.get_object()) \ + .annotate(team_id=F('registrations__team')).order_by('-valid', 'registrations__team__trigram').all() + + class TournamentExportCSVView(VolunteerMixin, DetailView): """ Export team information in a CSV file. diff --git a/registration/models.py b/registration/models.py index 2aaa19c..bf18892 100644 --- a/registration/models.py +++ b/registration/models.py @@ -496,6 +496,24 @@ class VolunteerRegistration(Registration): 'content': content, }) + payments = Payment.objects.filter(registrations__team__participation__tournament=tournament).all() + valid = payments.filter(valid=True).count() + pending = payments.filter(valid=None).count() + invalid = payments.filter(valid=False).count() + if pending + invalid > 0: + text = _("There are {valid} validated payments, {pending} pending and {invalid} invalid for the " + "tournament of {tournament}. You can check the status of the payments on the " + "payments list.") + url = reverse_lazy("participation:tournament_payments", args=(tournament.id,)) + content = format_lazy(text, valid=valid, pending=pending, invalid=invalid, tournament=tournament.name, + url=url) + informations.append({ + 'title': _("Payments"), + 'type': "info", + 'priority': 5, + 'content': content, + }) + return informations class Meta: diff --git a/registration/tables.py b/registration/tables.py index 5344804..0c97513 100644 --- a/registration/tables.py +++ b/registration/tables.py @@ -1,10 +1,11 @@ # Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later - +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ import django_tables2 as tables -from .models import Registration +from participation.models import Team +from .models import Payment, Registration class RegistrationTable(tables.Table): @@ -28,3 +29,41 @@ class RegistrationTable(tables.Table): model = Registration fields = ('last_name', 'user__first_name', 'user__email', 'type',) order_by = ('type', 'last_name', 'first_name',) + + +class PaymentTable(tables.Table): + """ + Table of all payments. + """ + team_id = tables.Column( + verbose_name=_("team").capitalize, + ) + + update_payment = tables.LinkColumn( + 'registration:update_payment', + accessor='id', + args=[tables.A("id")], + verbose_name=_("Update"), + orderable=False, + ) + + def render_team_id(self, value): + return Team.objects.get(id=value).trigram + + def render_amount(self, value): + return f"{value} €" + + def render_update_payment(self, record): + return mark_safe(f"") + + class Meta: + attrs = { + 'class': 'table table-condensed table-striped', + } + row_attrs = { + 'class': lambda record: ('table-success' if record.valid else + 'table-danger' if record.valid is False else 'table-warning'), + } + model = Payment + fields = ('registrations', 'team_id', 'type', 'amount', 'valid', 'update_payment',) + empty_text = _("No payment yet.")