1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2024-12-25 06:22:22 +00:00

Add payments table page

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-02-23 22:58:23 +01:00
parent 5cbc72b41f
commit 2c54f315f6
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
7 changed files with 218 additions and 80 deletions

View File

@ -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 <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 <a href=\"{url}\">page de "
"l'équipe</a>."
#: 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 <a href=\"{url}\">payments list</a>."
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 <a href=\"{url}\">liste des paiements</a>."
#: 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"

View File

@ -74,6 +74,14 @@
{% render_table teams %}
</div>
{% if user.registration.is_admin or user.registration in tournament.organizers.all %}
<div class="text-center">
<a href="{% url "participation:tournament_payments" pk=tournament.pk %}" class="btn btn-secondary">
<i class="fas fa-money-bill-wave"></i> {% trans "Access to payments list" %}
</a>
</div>
{% endif %}
{% if pools.data %}
<hr>

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% load django_tables2 i18n %}
{% block content %}
{% render_table table %}
<a href="{% url "participation:tournament_detail" pk=tournament.pk %}" class="btn btn-primary">
<i class="fas fa-long-arrow-alt-left"></i> {% trans "Back to tournament page" %}
</a>
{% endblock %}

View File

@ -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/<int:pk>/", TournamentDetailView.as_view(), name="tournament_detail"),
path("tournament/<int:pk>/update/", TournamentUpdateView.as_view(), name="tournament_update"),
path("tournament/<int:pk>/payments/", TournamentPaymentsView.as_view(), name="tournament_payments"),
path("tournament/<int:pk>/csv/", TournamentExportCSVView.as_view(), name="tournament_csv"),
path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),

View File

@ -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.

View File

@ -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 "
"<a href=\"{url}\">payments list</a>.")
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:

View File

@ -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"<button class='btn btn-secondary'><i class='fas fa-money-bill-wave'></i> {_('Update')}</button>")
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.")