399 lignes
15 KiB
Python
399 lignes
15 KiB
Python
# PIAF - Portail Internet pour les Activités familiales
|
|
# Copyright (C) 2018 Cliss XXI <tech@cliss21.org>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import decimal
|
|
from datetime import datetime
|
|
|
|
from django.views import generic
|
|
from django.shortcuts import HttpResponseRedirect, get_object_or_404
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.urls import reverse, reverse_lazy
|
|
from django.http import Http404, HttpResponseBadRequest
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from constance import config
|
|
|
|
from piaf.base import forms, utils, resthys, models, mixins
|
|
from eopayment import tipi
|
|
|
|
|
|
class RetrieveInvoice(generic.edit.FormView):
|
|
template_name = 'pages/retrieve_invoice.html'
|
|
success_url = reverse_lazy('prepare_invoice')
|
|
form_class = forms.RetrieveInvoiceForm
|
|
context = {}
|
|
|
|
def form_valid(self, form):
|
|
r = resthys.get('factures/{}'.format(form.facture['numero']))
|
|
|
|
self.context['invoice'] = r.json()
|
|
self.request.session['invoice'] = self.context['invoice']
|
|
self.request.session.pop('merged_invoices', None)
|
|
|
|
return super().form_valid(form)
|
|
|
|
|
|
class SetEmail(generic.edit.FormView):
|
|
success_url = reverse_lazy('prepare_invoice')
|
|
form_class = forms.EmailForm
|
|
http_method_names = ['post']
|
|
|
|
def form_valid(self, form):
|
|
try:
|
|
i = self.request.session.get('invoice')
|
|
titulaires = [
|
|
t['individu']
|
|
for t in i['compte_payeur']['famille']['rattachements_set']
|
|
if t['titulaire'] == 1
|
|
]
|
|
n = titulaires[0]['id']
|
|
resthys.put(
|
|
'individus/{}'.format(n),
|
|
data={'email': form.cleaned_data['email']},
|
|
)
|
|
except Exception as e:
|
|
if settings.DEBUG:
|
|
raise e
|
|
messages.error(
|
|
self.request, "Une erreur est survenue. Merci de ressayer "
|
|
"ultérieurement."
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class PrepareInvoice(mixins.HackyMessagesMixin, generic.edit.FormView):
|
|
template_name = 'pages/prepare_invoice.html'
|
|
success_url = reverse_lazy('prepare_merged_invoices')
|
|
form_class = forms.EmptyForm
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
f = self.request.session.get('invoice')
|
|
|
|
# refresh
|
|
r = resthys.get('factures/{}'.format(f['numero']))
|
|
|
|
context['invoice'] = r.json()
|
|
self.request.session['invoice'] = context['invoice']
|
|
|
|
if context['invoice']['compte_payeur']['email']:
|
|
if context['invoice']['solde'] > 0:
|
|
# create or recycle paiement
|
|
try:
|
|
paiement = models.TipiPaiement.objects.get(
|
|
pk=self.request.session['paiement'],
|
|
related_invoices=context['invoice']['numero'],
|
|
result=None,
|
|
)
|
|
except Exception:
|
|
paiement = models.TipiPaiement(
|
|
amount=context['invoice']['solde'],
|
|
email=context['invoice']['compte_payeur']['email'],
|
|
numcli=settings.NUMERO_REGIE,
|
|
objet='Facture no {}'.format(
|
|
context['invoice']['numero']
|
|
),
|
|
refdet='{:%Y%m%d%H%M%S}00{:06d}'.format(
|
|
datetime.now(), context['invoice']['numero']
|
|
),
|
|
saisie=settings.SAISIE_TIPI,
|
|
related_invoices=context['invoice']['numero'],
|
|
session_key=self.request.session.session_key
|
|
)
|
|
paiement.save()
|
|
self.request.session['paiement'] = paiement.id
|
|
|
|
# create pay link
|
|
p = tipi.Payment(
|
|
{
|
|
'numcli': paiement.numcli,
|
|
# 'automatic_return_url':
|
|
# self.request.build_absolute_uri(reverse('paid_invoice')),
|
|
'automatic_return_url':
|
|
'http://apps.villedeviolaines.fr/piaf/paid_invoice',
|
|
'service_url':
|
|
'https://www.tipi.budget.gouv.fr/tpa/paiement.web',
|
|
'saisie': paiement.saisie,
|
|
}
|
|
)
|
|
context['tipi'] = p.request(
|
|
amount=paiement.amount,
|
|
exer=datetime.today().year,
|
|
refdet=paiement.refdet,
|
|
objet=paiement.objet,
|
|
email=paiement.email,
|
|
)
|
|
else:
|
|
context['emailform'] = forms.EmailForm
|
|
|
|
# prestations
|
|
context['prestations'] = utils.regroup_prestations(
|
|
context['invoice']['prestations_set']
|
|
)
|
|
|
|
# FIXME to be removed
|
|
if 'emailform' not in context and 'paiement' in self.request.session:
|
|
paiement = models.TipiPaiement.objects.get(
|
|
pk=self.request.session['paiement'],
|
|
)
|
|
data = {
|
|
'numcli': paiement.numcli,
|
|
'exer': datetime.today().year,
|
|
'refdet': paiement.refdet,
|
|
'objet': paiement.objet,
|
|
'montant': paiement.amount,
|
|
'mel': paiement.email,
|
|
'saisie': paiement.saisie,
|
|
'numauto': '420042',
|
|
'dattrans': '01062018',
|
|
'heurtrans': '1142',
|
|
}
|
|
context['paid_form'] = forms.PaidInvoiceForm(
|
|
{**data, **{'resultrans': 'P'}}
|
|
)
|
|
context['unpaid_form'] = forms.PaidInvoiceForm(
|
|
{**data, **{'resultrans': 'R'}}
|
|
)
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
try:
|
|
return super().get(request, *args, **kwargs)
|
|
except Exception as e:
|
|
if settings.DEBUG:
|
|
raise e
|
|
messages.error(
|
|
self.request, "Une erreur est survenue. Merci de ressayer "
|
|
"ultérieurement."
|
|
)
|
|
return HttpResponseRedirect(reverse('retrieve_invoice'))
|
|
|
|
|
|
class PrepareMergedInvoices(PrepareInvoice):
|
|
http_method_names = ['get']
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
if 'merged_invoices' in self.request.session:
|
|
f = self.request.session['merged_invoices']
|
|
numeros = f['numeros']
|
|
invoices = []
|
|
else:
|
|
f = self.request.session.get('invoice')
|
|
numeros = [dic['numero'] for dic in f['autres_factures']]
|
|
invoices = [f]
|
|
|
|
# refresh
|
|
for n in numeros:
|
|
r = resthys.get('factures/{}'.format(n))
|
|
invoices.append(r.json())
|
|
|
|
# merging
|
|
context['invoice'] = utils.merge_invoices(invoices)
|
|
self.request.session['merged_invoices'] = context['invoice']
|
|
|
|
context['prestations'] = utils.regroup_prestations(
|
|
context['invoice']['prestations_set']
|
|
)
|
|
|
|
if context['invoice']['solde'] > 0:
|
|
# recycle paiement
|
|
paiement = models.TipiPaiement.objects.get(
|
|
pk=self.request.session['paiement'],
|
|
result=None,
|
|
)
|
|
# update paiement
|
|
paiement.objet = 'Factures no {}'.format(
|
|
' '.join([str(n) for n in context['invoice']['numeros']])
|
|
)
|
|
paiement.amount = decimal.Decimal(context['invoice']['solde'])
|
|
paiement.related_invoices = ','.join([
|
|
str(n) for n in context['invoice']['numeros']
|
|
])
|
|
paiement.refdet = paiement.refdet+'multi'
|
|
paiement.save()
|
|
|
|
# create pay link
|
|
p = tipi.Payment(
|
|
{
|
|
'numcli': paiement.numcli,
|
|
# 'automatic_return_url':
|
|
# self.request.build_absolute_uri(reverse('paid_invoice')),
|
|
'automatic_return_url':
|
|
'http://apps.villedeviolaines.fr/piaf/paid_invoice',
|
|
'service_url':
|
|
'https://www.tipi.budget.gouv.fr/tpa/paiement.web',
|
|
'saisie': paiement.saisie,
|
|
}
|
|
)
|
|
context['tipi'] = p.request(
|
|
amount=paiement.amount,
|
|
exer=datetime.today().year,
|
|
refdet=paiement.refdet,
|
|
objet=paiement.objet,
|
|
email=paiement.email,
|
|
)
|
|
|
|
# prestations
|
|
context['prestations'] = utils.regroup_prestations(
|
|
context['invoice']['prestations_set']
|
|
)
|
|
|
|
# FIXME to be removed
|
|
paiement = models.TipiPaiement.objects.get(
|
|
pk=self.request.session['paiement'],
|
|
)
|
|
data = {
|
|
'numcli': paiement.numcli,
|
|
'exer': datetime.today().year,
|
|
'refdet': paiement.refdet,
|
|
'objet': paiement.objet,
|
|
'montant': paiement.amount,
|
|
'mel': paiement.email,
|
|
'saisie': paiement.saisie,
|
|
'numauto': '420042',
|
|
'dattrans': '01062018',
|
|
'heurtrans': '1142',
|
|
}
|
|
context['paid_form'] = forms.PaidInvoiceForm(
|
|
{**data, **{'resultrans': 'P'}}
|
|
)
|
|
context['unpaid_form'] = forms.PaidInvoiceForm(
|
|
{**data, **{'resultrans': 'R'}}
|
|
)
|
|
return context
|
|
|
|
|
|
class PaidInvoice(mixins.RestrictToTipiHost, generic.edit.FormView):
|
|
http_method_names = ['post']
|
|
success_url = reverse_lazy('home')
|
|
form_class = forms.PaidInvoiceForm
|
|
|
|
@method_decorator(csrf_exempt)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def form_invalid(self, form):
|
|
return HttpResponseBadRequest()
|
|
|
|
def form_valid(self, form):
|
|
# We don't use response from eopaiement.tipi since django's form
|
|
# validation is stronger
|
|
try:
|
|
# objet isn't easily guessed; so this should succeed
|
|
# only if we built the object
|
|
paiement = models.TipiPaiement.set_from_post(form.cleaned_data)
|
|
except Exception as e:
|
|
if settings.DEBUG:
|
|
raise e
|
|
return HttpResponseBadRequest()
|
|
|
|
for n in paiement.get_related_invoices():
|
|
if paiement.is_paid():
|
|
try:
|
|
paiement.send_to_noethys()
|
|
models.HackyMessages(
|
|
level="success",
|
|
message="Paiement reçu pour la facture "
|
|
"n° {}".format(n),
|
|
session_key=paiement.session_key,
|
|
).save()
|
|
except Exception as e:
|
|
# When it fails we send a token by email
|
|
t, _ = models.PaiementToken.objects.get_or_create(
|
|
paiement=paiement
|
|
)
|
|
t.send_by_email(self.request)
|
|
# We still advertise the user that paiement has been
|
|
# receipted
|
|
models.HackyMessages(
|
|
level="success",
|
|
message="Paiement reçu pour la facture "
|
|
"n° {}. Néanmoins le paiement n'a pas pu être "
|
|
"enregistré comme prévu ; {} a été prévenu et "
|
|
"effectuera la correction.".format(
|
|
n,
|
|
config.SITE_CONTACT_NAME
|
|
),
|
|
session_key=paiement.session_key,
|
|
).save()
|
|
else:
|
|
models.HackyMessages(
|
|
level="warning",
|
|
message="Paiement refusé pour la facture "
|
|
"n° {}".format(n),
|
|
session_key=paiement.session_key,
|
|
).save()
|
|
|
|
return super().form_valid(form)
|
|
|
|
|
|
class RegisterPaiement(generic.edit.FormView):
|
|
template_name = 'pages/register_paiement.html'
|
|
success_url = reverse_lazy('home')
|
|
form_class = forms.EmptyForm
|
|
|
|
def get_object(self, **kwargs):
|
|
return get_object_or_404(models.PaiementToken, pk=self.kwargs['pk'])
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['token'] = self.get_object()
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
token = self.get_object()
|
|
paiement = token.paiement
|
|
if paiement.is_paid():
|
|
try:
|
|
paiement.send_to_noethys()
|
|
messages.success(
|
|
self.request, "Paiement enregistré avec succès !"
|
|
)
|
|
token.delete()
|
|
except Exception as e:
|
|
if settings.DEBUG:
|
|
raise e
|
|
messages.error(
|
|
self.request, "Une erreur est survenue. Merci de contacter "
|
|
"l'assistance."
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
|
|
# FIXME: to be removed
|
|
class UnpayInvoice(generic.RedirectView):
|
|
url = reverse_lazy('prepare_invoice')
|
|
http_method_names = ['get']
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if not settings.DEBUG:
|
|
raise Http404
|
|
if 'merged_invoices' in request.session:
|
|
invoice_nums = request.session.get('merged_invoices')['numeros']
|
|
self.url = reverse_lazy('prepare_merged_invoices')
|
|
else:
|
|
invoice_nums = [request.session.get('invoice')['numero']]
|
|
|
|
for n in invoice_nums:
|
|
resthys.put('unpay/{}'.format(n))
|
|
messages.success(
|
|
self.request,
|
|
"Paiement reverté pour la facture n° {}".format(n),
|
|
)
|
|
return super().get(request, *args, **kwargs)
|