mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-08-19 13:43:40 +00:00
subscripbtion voucher
This commit is contained in:
parent
0a8335c375
commit
48d8d7921d
10 changed files with 217 additions and 29 deletions
|
@ -30,8 +30,7 @@ from django.contrib import admin
|
|||
from reversion.admin import VersionAdmin
|
||||
|
||||
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
||||
from .models import CustomInvoice, CostEstimate
|
||||
from .tex import DocumentTemplate
|
||||
from .models import CustomInvoice, CostEstimate, DocumentTemplate
|
||||
|
||||
|
||||
class FactureAdmin(VersionAdmin):
|
||||
|
|
|
@ -48,9 +48,8 @@ from re2o.field_permissions import FieldPermissionFormMixin
|
|||
from re2o.mixins import FormRevMixin
|
||||
from .models import (
|
||||
Article, Paiement, Facture, Banque,
|
||||
CustomInvoice, Vente, CostEstimate
|
||||
CustomInvoice, Vente, CostEstimate, DocumentTemplate
|
||||
)
|
||||
from .tex import DocumentTemplate
|
||||
from .payment_methods import balance
|
||||
|
||||
|
||||
|
|
|
@ -236,11 +236,23 @@ class Facture(BaseInvoice):
|
|||
'control': self.can_change_control,
|
||||
}
|
||||
self.__original_valid = self.valid
|
||||
self.__original_control = self.control
|
||||
|
||||
def get_subscribtion(self):
|
||||
return self.vent_set.filter(
|
||||
Q(type_cotisation='All') |
|
||||
Q(type_cotisation='Cotisation')
|
||||
)
|
||||
|
||||
def is_subscribtion(self):
|
||||
return bool(self.get_subscribtion())
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super(Facture, self).save(*args, **kwargs)
|
||||
if not self.__original_valid and self.valid:
|
||||
send_mail_invoice(self)
|
||||
if self.is_subscribtion() and not self.__original_control and self.control:
|
||||
send_mail_voucher(self)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user) + ' ' + str(self.date)
|
||||
|
@ -255,6 +267,10 @@ def facture_post_save(**kwargs):
|
|||
user = facture.user
|
||||
user.set_active()
|
||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
||||
if facture.control:
|
||||
user = facture.user
|
||||
if user.is_adherent():
|
||||
user.notif_subscription_accepted()
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Facture)
|
||||
|
@ -935,3 +951,39 @@ def cotisation_post_delete(**_kwargs):
|
|||
"""
|
||||
regen('mac_ip_list')
|
||||
regen('mailing')
|
||||
|
||||
|
||||
class DocumentTemplate(RevMixin, AclMixin, models.Model):
|
||||
"""Represent a template in order to create documents such as invoice or
|
||||
subscribtion voucher.
|
||||
"""
|
||||
template = models.FileField(
|
||||
upload_to='templates/',
|
||||
verbose_name=_('template')
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('name')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("document template")
|
||||
verbose_name_plural = _("document templates")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
||||
|
||||
class Voucher(RevMixin, AclMixin, models.Model):
|
||||
"""A Subscription Voucher."""
|
||||
user = models.ForeignKey(
|
||||
'users.User',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("user")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("subscription voucher")
|
||||
|
||||
def __str__(self):
|
||||
return "voucher {} {}".format(self.user, self.date)
|
||||
|
|
|
@ -48,27 +48,6 @@ CACHE_PREFIX = getattr(settings, 'TEX_CACHE_PREFIX', 'render-tex')
|
|||
CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day
|
||||
|
||||
|
||||
class DocumentTemplate(RevMixin, AclMixin, models.Model):
|
||||
"""Represent a template in order to create documents such as invoice or
|
||||
subscribtion voucher.
|
||||
"""
|
||||
template = models.FileField(
|
||||
upload_to='templates/',
|
||||
verbose_name=_('template')
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('name')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("document template")
|
||||
verbose_name_plural = _("document templates")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
||||
|
||||
def render_invoice(_request, ctx={}):
|
||||
"""
|
||||
Render an invoice using some available information such as the current
|
||||
|
@ -92,6 +71,27 @@ def render_invoice(_request, ctx={}):
|
|||
return r
|
||||
|
||||
|
||||
def render_voucher(_request, ctx={}):
|
||||
"""
|
||||
Render a subscribtion voucher.
|
||||
"""
|
||||
options, _ = CotisationsOption.objects.get_or_create()
|
||||
filename = '_'.join([
|
||||
'voucher',
|
||||
slugify(ctx.get('asso_name', "")),
|
||||
slugify(ctx.get('recipient_name', "")),
|
||||
str(ctx.get('DATE', datetime.now()).year),
|
||||
str(ctx.get('DATE', datetime.now()).month),
|
||||
str(ctx.get('DATE', datetime.now()).day),
|
||||
])
|
||||
templatename = options.voucher_template.template.name.split('/')[-1]
|
||||
r = create_pdf(templatename, ctx)
|
||||
r['Content-Disposition'] = 'attachment; filename="{name}.pdf"'.format(
|
||||
name=filename
|
||||
)
|
||||
return r
|
||||
|
||||
|
||||
def create_pdf(template, ctx={}):
|
||||
"""Creates and returns a PDF from a LaTeX template using pdflatex.
|
||||
|
||||
|
|
|
@ -93,3 +93,56 @@ def send_mail_invoice(invoice):
|
|||
attachments=[('invoice.pdf', pdf, 'application/pdf')]
|
||||
)
|
||||
mail.send()
|
||||
|
||||
|
||||
def send_mail_voucher(invoice):
|
||||
"""Creates a voucher from an invoice and sends it by email to the client"""
|
||||
purchases_info = []
|
||||
for purchase in invoice.vente_set.all():
|
||||
purchases_info.append({
|
||||
'name': purchase.name,
|
||||
'price': purchase.prix,
|
||||
'quantity': purchase.number,
|
||||
'total_price': purchase.prix_total
|
||||
})
|
||||
|
||||
ctx = {
|
||||
'paid': True,
|
||||
'fid': invoice.id,
|
||||
'DATE': invoice.date,
|
||||
'recipient_name': "{} {}".format(
|
||||
invoice.user.name,
|
||||
invoice.user.surname
|
||||
),
|
||||
'address': invoice.user.room,
|
||||
'article': purchases_info,
|
||||
'total': invoice.prix_total(),
|
||||
'asso_name': AssoOption.get_cached_value('name'),
|
||||
'line1': AssoOption.get_cached_value('adresse1'),
|
||||
'line2': AssoOption.get_cached_value('adresse2'),
|
||||
'siret': AssoOption.get_cached_value('siret'),
|
||||
'email': AssoOption.get_cached_value('contact'),
|
||||
'phone': AssoOption.get_cached_value('telephone'),
|
||||
'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
|
||||
}
|
||||
|
||||
pdf = create_pdf('cotisations/factures.tex', ctx)
|
||||
template = get_template('cotisations/email_invoice')
|
||||
|
||||
ctx = {
|
||||
'name': "{} {}".format(
|
||||
invoice.user.name,
|
||||
invoice.user.surname
|
||||
),
|
||||
'contact_mail': AssoOption.get_cached_value('contact'),
|
||||
'asso_name': AssoOption.get_cached_value('name')
|
||||
}
|
||||
|
||||
mail = EmailMessage(
|
||||
'Votre facture / Your invoice',
|
||||
template.render(ctx),
|
||||
GeneralOption.get_cached_value('email_from'),
|
||||
[invoice.user.get_mail],
|
||||
attachments=[('invoice.pdf', pdf, 'application/pdf')]
|
||||
)
|
||||
mail.send()
|
||||
|
|
|
@ -69,7 +69,8 @@ from .models import (
|
|||
Banque,
|
||||
CustomInvoice,
|
||||
BaseInvoice,
|
||||
CostEstimate
|
||||
CostEstimate,
|
||||
DocumentTemplate
|
||||
)
|
||||
from .forms import (
|
||||
FactureForm,
|
||||
|
@ -87,7 +88,7 @@ from .forms import (
|
|||
DocumentTemplateForm,
|
||||
DelDocumentTemplateForm
|
||||
)
|
||||
from .tex import render_invoice, escape_chars, DocumentTemplate
|
||||
from .tex import render_invoice, escape_chars
|
||||
from .payment_methods.forms import payment_method_factory
|
||||
from .utils import find_payment_method
|
||||
|
||||
|
|
38
preferences/migrations/0057_cotisationsoption.py
Normal file
38
preferences/migrations/0057_cotisationsoption.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2019-01-03 19:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import re2o.mixins
|
||||
|
||||
|
||||
def initialize_invoice_template(apps, schema_editor):
|
||||
CotisationsOption = apps.get_model('preferences', 'CotisationsOption')
|
||||
DocumentTemplate = apps.get_model('cotisations', 'DocumentTemplate')
|
||||
CotisationsOption.objects.create(
|
||||
invoice_template=DocumentTemplate.objects.first()
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0039_documenttemplate'),
|
||||
('preferences', '0056_4_radiusoption'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CotisationsOption',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('invoice_template', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='invoice_template', to='cotisations.DocumentTemplate', verbose_name='Template for invoices')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'cotisations options',
|
||||
},
|
||||
bases=(re2o.mixins.AclMixin, models.Model),
|
||||
),
|
||||
migrations.RunPython(initialize_invoice_template),
|
||||
]
|
|
@ -698,4 +698,9 @@ class CotisationsOption(AclMixin, PreferencesModel):
|
|||
related_name="invoice_template",
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
voucher_template = models.OneToOneField(
|
||||
'cotisations.DocumentTemplate',
|
||||
verbose_name=_("Template for subscription voucher"),
|
||||
related_name="voucher_template",
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
|
|
@ -663,7 +663,26 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
|
|||
)
|
||||
return
|
||||
|
||||
def reset_passwd_mail(self, request):
|
||||
def notif_subscription_accepted(self):
|
||||
"""Send an email when the subscription has been accepted"""
|
||||
template = loader.get_template('users/email_subscription_accepted')
|
||||
mailmessageoptions, _created = MailMessageOption\
|
||||
.objects.get_or_create()
|
||||
context = Context({
|
||||
'nom': self.get_full_name(),
|
||||
'asso_name': AssoOption.get_cached_value('name'),
|
||||
'asso_email': AssoOption.get_cached_value('contact'),
|
||||
})
|
||||
send_mail(
|
||||
'Votre inscription a été validée / Your subscription has been accepted',
|
||||
'',
|
||||
GeneralOption.get_cached_value('email_from'),
|
||||
[self.email],
|
||||
html_message=template.render(context)
|
||||
)
|
||||
return
|
||||
|
||||
def reset_passwd_mail(self, request):
|
||||
""" Prend en argument un request, envoie un mail de
|
||||
réinitialisation de mot de pass """
|
||||
req = Request()
|
||||
|
|
22
users/templates/users/email_subscription_accepted
Normal file
22
users/templates/users/email_subscription_accepted
Normal file
|
@ -0,0 +1,22 @@
|
|||
<p>Bonjour {{nom}} !</p>
|
||||
|
||||
<p>Nous vous informons que votre cotisation auprès de {{asso_name}} a été acceptée. Vous voilà donc membre de l'association.</p>
|
||||
|
||||
<p>Vous trouverez en pièce jointe un reçu.</p>
|
||||
|
||||
<p>Pour nous faire part de toute remarque, suggestion ou problème vous pouvez nous envoyer un mail à {{asso_email}}.</p>
|
||||
|
||||
<p>À bientôt,<br>
|
||||
L'équipe de {{asso_name}}.</p>
|
||||
|
||||
<p>---</p>
|
||||
|
||||
<p>Your subscription to {{asso_name}} has just been accepted. You are now a full member of {{asso_name}}.
|
||||
|
||||
<p>You will find with this email a subscription voucher.</p>
|
||||
|
||||
<p>For any information, suggestion or problem, you can contact us via email at<br>
|
||||
{{asso_email}}.</p>
|
||||
|
||||
<p>Regards,<br>
|
||||
The {{asso_name}} team.</p>
|
Loading…
Reference in a new issue