Browse Source

Minor invoice pdf generation improvments

Fabs 10 years ago
parent
commit
5cd41433c7
4 changed files with 24 additions and 18 deletions
  1. 3 3
      coin/billing/admin.py
  2. 14 14
      coin/billing/models.py
  3. 1 1
      coin/billing/urls.py
  4. 6 0
      coin/utils.py

+ 3 - 3
coin/billing/admin.py

@@ -42,15 +42,15 @@ class PaymentInline(admin.StackedInline):
 
 
 
 
 class InvoiceAdmin(admin.ModelAdmin):
 class InvoiceAdmin(admin.ModelAdmin):
-    list_display = ('number', 'date', 'status', 'amount', 'member', 'validated')
+    list_display = ('number', 'date', 'status', 'amount', 'member', 'is_validated')
     list_display_links = ('number', 'date')
     list_display_links = ('number', 'date')
     inlines = [InvoiceDetailInline, PaymentInline]
     inlines = [InvoiceDetailInline, PaymentInline]
     fields = (('number', 'date', 'status'),
     fields = (('number', 'date', 'status'),
        ('date_due'),
        ('date_due'),
        ('member'),
        ('member'),
        ('amount','amount_paid'),
        ('amount','amount_paid'),
-       ('validated', 'pdf'))
-    readonly_fields = ('amount','amount_paid','validated','pdf')
+       ('is_validated', 'pdf'))
+    readonly_fields = ('amount','amount_paid','is_validated','pdf')
     form = autocomplete_light.modelform_factory(Invoice)
     form = autocomplete_light.modelform_factory(Invoice)
 
 
     def get_formsets(self, request, obj=None):
     def get_formsets(self, request, obj=None):

+ 14 - 14
coin/billing/models.py

@@ -8,17 +8,13 @@ from decimal import Decimal
 from django.db import models
 from django.db import models
 from django.db.models.signals import post_save
 from django.db.models.signals import post_save
 from django.dispatch import receiver
 from django.dispatch import receiver
-from django.core.files.storage import FileSystemStorage
-from django.conf import settings
 
 
 from coin.offers.models import OfferSubscription
 from coin.offers.models import OfferSubscription
 from coin.members.models import Member
 from coin.members.models import Member
 from coin.html2pdf import render_as_pdf
 from coin.html2pdf import render_as_pdf
+from coin.utils import private_files_storage
 
 
 
 
-# Stockage des factures
-invoices_storage = FileSystemStorage(location=os.path.join(settings.PRIVATE_FILES_ROOT, 'invoices/'))
-
 def next_invoice_number():
 def next_invoice_number():
     "Détermine un numéro de facture aléatoire"
     "Détermine un numéro de facture aléatoire"
     return u'%s%02i-%i-%i' % (datetime.date.today().year,
     return u'%s%02i-%i-%i' % (datetime.date.today().year,
@@ -26,6 +22,10 @@ def next_invoice_number():
                               random.randrange(100, 999),
                               random.randrange(100, 999),
                               random.randrange(100, 999))
                               random.randrange(100, 999))
 
 
+def invoice_pdf_filename(instance, filename):
+    "Nom du fichier pdf à stocker pour les factures"
+    return u'%d_%s_%s.pdf' % (instance.member.id, instance.number, uuid.uuid4())
+
 class Invoice(models.Model):
 class Invoice(models.Model):
 
 
     INVOICES_STATUS_CHOICES = (
     INVOICES_STATUS_CHOICES = (
@@ -34,7 +34,7 @@ class Invoice(models.Model):
         ('trouble', u'Litige')
         ('trouble', u'Litige')
     )
     )
 
 
-    validated = models.BooleanField(default=False, verbose_name='Facture validée')
+    validated = models.BooleanField(default=False, verbose_name='Validée')
     number = models.CharField(max_length=25,
     number = models.CharField(max_length=25,
                               default=next_invoice_number,
                               default=next_invoice_number,
                               unique=True,
                               unique=True,
@@ -53,9 +53,9 @@ class Invoice(models.Model):
                                related_name='invoices',
                                related_name='invoices',
                                verbose_name='Membre',
                                verbose_name='Membre',
                                on_delete=models.SET_NULL)
                                on_delete=models.SET_NULL)
-    pdf = models.FileField(storage=invoices_storage,
+    pdf = models.FileField(storage=private_files_storage, upload_to=invoice_pdf_filename,
                            null=True, blank=True,
                            null=True, blank=True,
-                           verbose_name=u'Facture en PDF')
+                           verbose_name=u'PDF')
 
 
     def amount(self):
     def amount(self):
         "Calcul le montant de la facture en fonction des éléments de détails"
         "Calcul le montant de la facture en fonction des éléments de détails"
@@ -90,20 +90,20 @@ class Invoice(models.Model):
     def generate_pdf(self):
     def generate_pdf(self):
         "Make and store a pdf file for the invoice"
         "Make and store a pdf file for the invoice"
         pdf_file = render_as_pdf('billing/invoice_pdf.html', {"invoice": self})
         pdf_file = render_as_pdf('billing/invoice_pdf.html', {"invoice": self})
-        self.pdf.save(u'%s_%s.pdf' % (self.number, uuid.uuid4()), pdf_file)
+        self.pdf.save(u'%s.pdf' % self.number, pdf_file)
 
 
     def validate(self):
     def validate(self):
         """
         """
         Switch invoice to validate mode. This set to False the draft field
         Switch invoice to validate mode. This set to False the draft field
         and generate the pdf
         and generate the pdf
         """
         """
-        # if not self.is_validated():
-        self.validated = True
-        self.save()
-        self.generate_pdf()
+        if not self.is_validated():
+            self.validated = True
+            self.save()
+            self.generate_pdf()
 
 
     def is_validated(self):
     def is_validated(self):
-        return self.validated and bool(self.pdf)
+        return self.validated and bool(self.pdf) and private_files_storage.exists(self.pdf.name)
 
 
     def __unicode__(self):
     def __unicode__(self):
         return u'#%s %0.2f€ %s' % (self.number, self.amount(), self.date_due)
         return u'#%s %0.2f€ %s' % (self.number, self.amount(), self.date_due)

+ 1 - 1
coin/billing/urls.py

@@ -4,7 +4,7 @@ from coin.billing import views
 
 
 urlpatterns = patterns(
 urlpatterns = patterns(
     '',
     '',
-    url(r'^invoice/(?P<id>.+).pdf$', views.invoice_pdf, name="invoice_pdf"),
+    url(r'^invoice/(?P<id>.+)/pdf$', views.invoice_pdf, name="invoice_pdf"),
     url(r'^invoice/(?P<id>.+)$', views.invoice, name="invoice"),
     url(r'^invoice/(?P<id>.+)$', views.invoice, name="invoice"),
     # url(r'^invoice/(?P<id>.+)/validate$', views.invoice_validate, name="invoice_validate"),
     # url(r'^invoice/(?P<id>.+)/validate$', views.invoice_validate, name="invoice_validate"),
     
     

+ 6 - 0
coin/utils.py

@@ -6,6 +6,12 @@ import base64
 import html2text
 import html2text
 from django.core.mail import EmailMultiAlternatives
 from django.core.mail import EmailMultiAlternatives
 from django.template import TemplateDoesNotExist
 from django.template import TemplateDoesNotExist
+from django.core.files.storage import FileSystemStorage
+from django.conf import settings
+
+
+# Stockage des fichiers privés (comme les factures par exemple)
+private_files_storage = FileSystemStorage(location=settings.PRIVATE_FILES_ROOT)
 
 
 
 
 def str_or_none(obj):
 def str_or_none(obj):