formation-re2o-cotisations/presentation.tex
Hugo Levy-Falk f3df8d70e1 First commit
2021-07-10 21:42:45 +02:00

327 lines
9.8 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass[10pt,xcolor={table,dvipsnames},t, aspectratio=169]{beamer}
\usepackage{minted}
\usetheme{rmrf}
\title{Re2o et les cotisations}
\subtitle{Rézo Rennes et Metz Fédérés}
\author{Klafyvel}
\date{10/07/2021}
\begin{document}
\begin{frame}
\titlepage
\end{frame}
\begin{frame}{Sommaire}
\tableofcontents
\end{frame}
\begin{frame}
\vspace{2cm}
\centering{\Huge\textbf{Contrainte principale :}\\Ne pas faire de la merde avec les sous des adhérents.}
\end{frame}
\section{Les principales structures de données}
\begin{frame}[fragile]{L'objet Facture}
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{itemize}
\item<1-> Représente une facture;
\item<2-> Visible par les adhérents;
\item<3-> Est censé être controlée par les trésoriers.
\end{itemize}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
class Facture(BaseInvoice):
user = models.ForeignKey("users.User",...)
paiement = models.ForeignKey("Paiement", ...)
banque = models.ForeignKey("Banque", ...)
cheque = models.CharField(...)
valid = models.BooleanField(...)
control = models.BooleanField(...)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{L'objet Vente}
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{itemize}
\item Représente une vente de \verb|number| articles(s) au prix unitaire \verb|prix|;
\item Stocke une information sur la durée d'adhésion / cotisation.
\end{itemize}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
class Vente(RevMixin, AclMixin, models.Model):
facture = models.ForeignKey("BaseInvoice", ...)
number = models.IntegerField(...)
name = models.CharField(...)
prix = models.DecimalField(...)
duration_connection = models.PositiveIntegerField(...)
duration_days_connection = models.PositiveIntegerField(...)
duration_membership = models.PositiveIntegerField(...)
duration_days_membership = models.PositiveIntegerField(...)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{L'objet Article}
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{itemize}
\item Une recette pour créer des \verb|Vente|s;
\item On ne lie pas directement les ventes aux articles, on peut supprimer les articles sans contrainte.
\end{itemize}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
class Article(RevMixin, AclMixin, models.Model):
name = models.CharField(...)
prix = models.DecimalField(...)
duration_membership = models.PositiveIntegerField(...)
duration_days_membership = models.PositiveIntegerField(...)
duration_connection = models.PositiveIntegerField(...)
duration_days_connection = models.PositiveIntegerField(...)
need_membership = models.BooleanField(...)
type_user = models.CharField(...)
available_for_everyone = models.BooleanField(...)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{L'objet Cotisation}
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{itemize}
\item Information sur les adhésions / cotisations des utilisateurs;
\item Cest ce qui est utilisé par lobjet \verb|User| pour savoir si un utilisateur est adhérent.
\end{itemize}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
class Cotisation(RevMixin, AclMixin, models.Model):
vente = models.OneToOneField("Vente",...)
date_start_con = models.DateTimeField(...)
date_end_con = models.DateTimeField(...)
date_start_memb = models.DateTimeField(...)
date_end_memb = models.DateTimeField(...)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{L'objet Paiement}
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{itemize}
\item Enregistre un moyen de paiement;
\item On peut y brancher des méthodes de paiement personnalisées.
\end{itemize}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
class Paiement(RevMixin, AclMixin, models.Model):
moyen = models.CharField(...)
available_for_everyone = models.BooleanField(...)
is_balance = models.BooleanField(...)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\section{La vue new\_facture}
\begin{frame}[fragile]
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
@login_required
@can_create(Facture)
@can_edit(User)
def new_facture(request, user, userid):
"""De la doc..."""
invoice = Facture(user=user)
article_list = Article.objects.filter(...)
invoice_form = FactureForm(...)
article_formset = formset_factory(SelectArticleForm)(...)
if invoice_form.is_valid() and article_formset.is_valid():
# On verra après
# Gestion du solde
p = Paiement.objects.filter(is_balance=True)
if len(p) and p[0].can_use_payment(request.user):
balance = user.solde
else:
balance = None
return form(...)
\end{minted}
\end{column}
\begin{column}{0.5\textwidth}
Vue assez classique de formulaire Django.
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{À l'intérieur de ce if}
\begin{minted}[fontsize=\footnotesize,]{python}
new_invoice_instance = invoice_form.save(commit=False)
articles = article_formset
# Check if at least one article has been selected
if any(art.cleaned_data for art in articles):
# Encore un peu de patience, slide suivante
else :
messages.error(request, _("You need to choose at least one article."))
\end{minted}
\end{frame}
\begin{frame}
\vspace{2cm}
\centering{\Huge\textbf{Roulement de tambours...}}
\end{frame}
\begin{frame}[fragile]
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
# Building a purchase for each article sold
purchases = []
total_price = 0
for art_item in articles:
if art_item.cleaned_data:
article = art_item.cleaned_data["article"]
quantity = art_item.cleaned_data["quantity"]
total_price += article.prix * quantity
new_purchase = Vente(...)
purchases.append(new_purchase)
p = find_payment_method(new_invoice_instance.paiement)
if hasattr(p, "check_price"):
price_ok, msg = p.check_price(total_price, user)
invoice_form.add_error(None, msg)
else:
price_ok = True
\end{minted}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
if price_ok:
new_invoice_instance.save()
# Saving purchases so the invoice can find them. Invoice
# will modify them after being validated to put the right dates.
for p in purchases:
p.facture = new_invoice_instance
p.save()
return new_invoice_instance.paiement.end_payment(
new_invoice_instance, request
)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\begin{frame}[fragile]{Le retour du modèle Paiement}
\begin{columns}
\begin{column}{0.5\textwidth}
Pour finir un paiement,
\begin{itemize}
\item Si une méthode de paiement personnalisée existe et que la transisition n'est pas explicitement interdite, on branche;
\item Sinon on valide la facture, on affiche un message et on redirige.
\end{itemize}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
def end_payment(self, invoice, request,
use_payment_method=True):
payment_method = find_payment_method(self)
if payment_method is not None \
and use_payment_method:
return payment_method.end_payment(invoice,
request)
# So make this invoice valid, trigger send mail
invoice.valid = True
invoice.save(request=request)
# Du code pas intéressant
return redirect(...)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\section{Un exemple simple : FreePayment}
\begin{frame}[fragile]
\begin{columns}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
class FreePayment(PaymentMethodMixin, models.Model):
"""..."""
class Meta:
verbose_name = _("Free payment")
payment = models.OneToOneField(
Paiement,
on_delete=models.CASCADE,
related_name="payment_method_free",
editable=False,
)
\end{minted}
\end{column}
\begin{column}{0.5\textwidth}
\begin{minted}[fontsize=\footnotesize,]{python}
def end_payment(self, invoice, request):
"""Ends the payment normally.
"""
return invoice.paiement.end_payment(invoice,
request,
use_payment_method=False
)
def check_price(self, price, user, *args, **kwargs):
"""Checks that the price meets the requirement
to be paid with user balance.
"""
return (
price == 0,
_("You can't pay this invoice for free.")
)
\end{minted}
\end{column}
\end{columns}
\end{frame}
\section{Un exemple intéressant : ComnPay}
\begin{frame}{Déroulé}
\begin{itemize}
\item<2-> Le end\_payment construit une requête spéciale pour que l'utilisateur l'envoie vers comnpay. La facture est invalide;
\item<3-> L'utilisateur fait son affaire avec ComnPay;
\item<4-> ComnPay notifie re2o que le paiement a été effectué. La facture est validée;
\item<5-> L'utilisateur est redirigé vers re2o.
\end{itemize}
\end{frame}
\begin{frame}
\vspace{2cm}
\centering{\Huge\textbf{Un petit tour dans le code.}}
\end{frame}
\section{Conclusion}
\begin{frame}{Conclusion}
\begin{itemize}
\item Le système de paiement est modulable;
\item Mais du coup il est complexe;
\item Pour intégrer HelloAsso il faudrait s'inspirer du système ComnPay.
\end{itemize}
\end{frame}
\end{document}