mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 11:23:10 +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 reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
||||||
from .models import CustomInvoice, CostEstimate
|
from .models import CustomInvoice, CostEstimate, DocumentTemplate
|
||||||
from .tex import DocumentTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class FactureAdmin(VersionAdmin):
|
class FactureAdmin(VersionAdmin):
|
||||||
|
|
|
@ -48,9 +48,8 @@ from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from .models import (
|
from .models import (
|
||||||
Article, Paiement, Facture, Banque,
|
Article, Paiement, Facture, Banque,
|
||||||
CustomInvoice, Vente, CostEstimate
|
CustomInvoice, Vente, CostEstimate, DocumentTemplate
|
||||||
)
|
)
|
||||||
from .tex import DocumentTemplate
|
|
||||||
from .payment_methods import balance
|
from .payment_methods import balance
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -236,11 +236,23 @@ class Facture(BaseInvoice):
|
||||||
'control': self.can_change_control,
|
'control': self.can_change_control,
|
||||||
}
|
}
|
||||||
self.__original_valid = self.valid
|
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):
|
def save(self, *args, **kwargs):
|
||||||
super(Facture, self).save(*args, **kwargs)
|
super(Facture, self).save(*args, **kwargs)
|
||||||
if not self.__original_valid and self.valid:
|
if not self.__original_valid and self.valid:
|
||||||
send_mail_invoice(self)
|
send_mail_invoice(self)
|
||||||
|
if self.is_subscribtion() and not self.__original_control and self.control:
|
||||||
|
send_mail_voucher(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.user) + ' ' + str(self.date)
|
return str(self.user) + ' ' + str(self.date)
|
||||||
|
@ -255,6 +267,10 @@ def facture_post_save(**kwargs):
|
||||||
user = facture.user
|
user = facture.user
|
||||||
user.set_active()
|
user.set_active()
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
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)
|
@receiver(post_delete, sender=Facture)
|
||||||
|
@ -935,3 +951,39 @@ def cotisation_post_delete(**_kwargs):
|
||||||
"""
|
"""
|
||||||
regen('mac_ip_list')
|
regen('mac_ip_list')
|
||||||
regen('mailing')
|
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
|
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={}):
|
def render_invoice(_request, ctx={}):
|
||||||
"""
|
"""
|
||||||
Render an invoice using some available information such as the current
|
Render an invoice using some available information such as the current
|
||||||
|
@ -92,6 +71,27 @@ def render_invoice(_request, ctx={}):
|
||||||
return r
|
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={}):
|
def create_pdf(template, ctx={}):
|
||||||
"""Creates and returns a PDF from a LaTeX template using pdflatex.
|
"""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')]
|
attachments=[('invoice.pdf', pdf, 'application/pdf')]
|
||||||
)
|
)
|
||||||
mail.send()
|
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,
|
Banque,
|
||||||
CustomInvoice,
|
CustomInvoice,
|
||||||
BaseInvoice,
|
BaseInvoice,
|
||||||
CostEstimate
|
CostEstimate,
|
||||||
|
DocumentTemplate
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
FactureForm,
|
FactureForm,
|
||||||
|
@ -87,7 +88,7 @@ from .forms import (
|
||||||
DocumentTemplateForm,
|
DocumentTemplateForm,
|
||||||
DelDocumentTemplateForm
|
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 .payment_methods.forms import payment_method_factory
|
||||||
from .utils import find_payment_method
|
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",
|
related_name="invoice_template",
|
||||||
on_delete=models.PROTECT,
|
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
|
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
|
""" Prend en argument un request, envoie un mail de
|
||||||
réinitialisation de mot de pass """
|
réinitialisation de mot de pass """
|
||||||
req = Request()
|
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