\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 C’est ce qui est utilisé par l’objet \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}
