Browse Source

Merge branch 'master' of git.illyse.org:coin

Oriane 11 years ago
parent
commit
ad26a5463c

+ 11 - 2
coin/billing/admin.py

@@ -1,20 +1,29 @@
 # -*- coding: utf-8 -*-
 from django.contrib import admin
 from coin.billing.models import Invoice, InvoiceDetail, Payment
+import autocomplete_light
 
 
 class InvoiceDetailInline(admin.StackedInline):
     model = InvoiceDetail
     extra = 0
+    fields = (('label', 'amount', 'quantity', 'tax', 'offer'),)
 
 
 class PaymentInline(admin.StackedInline):
     model = Payment
     extra = 0
+    fields = (('date', 'payment_mean', 'amount'),)
 
 
 class InvoiceAdmin(admin.ModelAdmin):
-    inlines = [InvoiceDetailInline, PaymentInline]
+	list_display = ('number', 'date', 'status', 'amount', 'member')
+	list_display_links = ('number', 'date')
+	inlines = [InvoiceDetailInline, PaymentInline]
+	fields = (('number', 'date', 'status'),
+			  ('period_from', 'period_to', 'date_due'),
+			  'member')
+	form = autocomplete_light.modelform_factory(Invoice)
 
 
-admin.site.register(Invoice,InvoiceAdmin)
+admin.site.register(Invoice, InvoiceAdmin)

+ 88 - 0
coin/billing/migrations/0005_auto__add_field_invoice_member.py

@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Invoice.member'
+        db.add_column(u'billing_invoice', 'member',
+                      self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['members.Member'], null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Invoice.member'
+        db.delete_column(u'billing_invoice', 'member_id')
+
+
+    models = {
+        u'billing.invoice': {
+            'Meta': {'object_name': 'Invoice'},
+            'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            'date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today', 'null': 'True'}),
+            'date_due': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 28, 0, 0)', 'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'member': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['members.Member']", 'null': 'True'}),
+            'number': ('django.db.models.fields.CharField', [], {'max_length': '25'}),
+            'period_from': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 1, 0, 0)', 'null': 'True'}),
+            'period_to': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 28, 0, 0)', 'null': 'True'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'open'", 'max_length': '50'})
+        },
+        u'billing.invoicedetail': {
+            'Meta': {'object_name': 'InvoiceDetail'},
+            'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'invoice': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'details'", 'to': u"orm['billing.Invoice']"}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'offer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['offers.Offer']", 'null': 'True'}),
+            'quantity': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'tax': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'})
+        },
+        u'billing.payment': {
+            'Meta': {'object_name': 'Payment'},
+            'amount': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2'}),
+            'date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'invoce': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['billing.Invoice']"}),
+            'payment_means': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+        },
+        u'members.member': {
+            'Meta': {'object_name': 'Member'},
+            'address': ('django.db.models.fields.TextField', [], {}),
+            'city': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'country': ('django.db.models.fields.CharField', [], {'default': "'France'", 'max_length': '200'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '254'}),
+            'entry_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'home_phone_number': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'ldap_cn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'mobile_phone_number': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}),
+            'organization_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '15'}),
+            'resign_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'non_adherent'", 'max_length': '50'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'personne_physique'", 'max_length': '20'})
+        },
+        u'offers.offer': {
+            'Meta': {'object_name': 'Offer'},
+            'billing_period': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'initial_fees': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'period_fees': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            'service': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['offers.Service']"})
+        },
+        u'offers.service': {
+            'Meta': {'object_name': 'Service'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['billing']

+ 96 - 0
coin/billing/migrations/0006_auto__del_field_payment_payment_means__add_field_payment_payment_mean.py

@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Deleting field 'Payment.payment_means'
+        db.delete_column(u'billing_payment', 'payment_means')
+
+        # Adding field 'Payment.payment_mean'
+        db.add_column(u'billing_payment', 'payment_mean',
+                      self.gf('django.db.models.fields.CharField')(max_length=100, null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Adding field 'Payment.payment_means'
+        db.add_column(u'billing_payment', 'payment_means',
+                      self.gf('django.db.models.fields.CharField')(max_length=100, null=True),
+                      keep_default=False)
+
+        # Deleting field 'Payment.payment_mean'
+        db.delete_column(u'billing_payment', 'payment_mean')
+
+
+    models = {
+        u'billing.invoice': {
+            'Meta': {'object_name': 'Invoice'},
+            'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            'date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today', 'null': 'True'}),
+            'date_due': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 28, 0, 0)', 'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'member': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['members.Member']", 'null': 'True', 'blank': 'True'}),
+            'number': ('django.db.models.fields.CharField', [], {'max_length': '25'}),
+            'period_from': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 1, 0, 0)', 'null': 'True'}),
+            'period_to': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 28, 0, 0)', 'null': 'True'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'open'", 'max_length': '50'})
+        },
+        u'billing.invoicedetail': {
+            'Meta': {'object_name': 'InvoiceDetail'},
+            'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'invoice': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'details'", 'to': u"orm['billing.Invoice']"}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'offer': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['offers.Offer']", 'null': 'True', 'blank': 'True'}),
+            'quantity': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'tax': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'})
+        },
+        u'billing.payment': {
+            'Meta': {'object_name': 'Payment'},
+            'amount': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2'}),
+            'date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'invoce': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['billing.Invoice']"}),
+            'payment_mean': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+        },
+        u'members.member': {
+            'Meta': {'object_name': 'Member'},
+            'address': ('django.db.models.fields.TextField', [], {}),
+            'city': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'country': ('django.db.models.fields.CharField', [], {'default': "'France'", 'max_length': '200'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '254'}),
+            'entry_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'home_phone_number': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'ldap_cn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'mobile_phone_number': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}),
+            'organization_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '15'}),
+            'resign_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'non_adherent'", 'max_length': '50'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'personne_physique'", 'max_length': '20'})
+        },
+        u'offers.offer': {
+            'Meta': {'object_name': 'Offer'},
+            'billing_period': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'initial_fees': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'period_fees': ('django.db.models.fields.DecimalField', [], {'max_digits': '5', 'decimal_places': '2'}),
+            'service': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['offers.Service']"})
+        },
+        u'offers.service': {
+            'Meta': {'object_name': 'Service'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['billing']

+ 19 - 4
coin/billing/models.py

@@ -3,6 +3,7 @@ import datetime
 import calendar
 from django.db import models
 from coin.offers.models import Offer
+from coin.members.models import Member
 
 
 class Invoice(models.Model):
@@ -38,6 +39,8 @@ class Invoice(models.Model):
                  datetime.timedelta(days=1)),
         null=True,
         verbose_name=u'Date d\'échéance de paiement')
+    member = models.ForeignKey(Member, null=True, blank=True, default=None,
+                               verbose_name='Membre')
 
     def __unicode__(self):
         return u'#%s %s€ %s' % (self.number, self.amount, self.date_due)
@@ -53,8 +56,10 @@ class InvoiceDetail(models.Model):
                                  verbose_name='Montant')
     quantity = models.IntegerField(null=True, verbose_name=u'Quantité')
     tax = models.IntegerField(null=True, default=0, verbose_name='TVA')
-    invoice = models.ForeignKey(Invoice, verbose_name='Facture')
-    offer = models.ForeignKey(Offer, null=True, verbose_name='Offre')
+    invoice = models.ForeignKey(Invoice, verbose_name='Facture',
+                                related_name='details')
+    offer = models.ForeignKey(Offer, null=True, blank=True, default=None,
+                              verbose_name='Offre')
 
     def __unicode__(self):
         return self.label
@@ -64,8 +69,18 @@ class InvoiceDetail(models.Model):
 
 
 class Payment(models.Model):
-    payment_means = models.CharField(max_length=100, null=True,
-                                     verbose_name='Moyen de paiement')
+
+    PAYMENT_MEAN_CHOICES = (
+        ('cash', u'Espèces'),
+        ('check', u'Chèque'),
+        ('transfer', u'Virement'),
+        ('other', u'Autre')
+    )
+
+    payment_mean = models.CharField(max_length=100, null=True,
+                                    default='transfer',
+                                    choices=PAYMENT_MEAN_CHOICES,
+                                    verbose_name='Moyen de paiement')
     amount = models.DecimalField(max_digits=7, decimal_places=2, null=True,
                                  verbose_name='Montant')
     date = models.DateField(default=datetime.date.today)

+ 51 - 0
coin/billing/process_latex.py

@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+from subprocess import call, PIPE
+from os import remove, rename
+from os.path import dirname
+from tempfile import NamedTemporaryFile
+from django.template import loader, Context
+from django.conf import settings
+
+
+def process_latex(template, context={}, type='pdf', outfile=None):
+    """
+    Processes a template as a LaTeX source file.
+    Output is either being returned or stored in outfile.
+    At the moment only pdf output is supported.
+    """
+
+    t = loader.get_template(template)
+    c = Context(context)
+    r = t.render(c)
+
+    tex = NamedTemporaryFile()
+    tex.write(r.encode('utf-8'))
+    tex.flush()
+    base = tex.name
+    items = "log aux pdf dvi png".split()
+    names = dict((x, '%s.%s' % (base, x)) for x in items)
+    output = names[type]
+
+    if type == 'pdf' or type == 'dvi':
+        pdflatex(base, type)
+    elif type == 'png':
+        pdflatex(base, 'dvi')
+        call(['dvipng', '-bg', '-transparent',
+              names['dvi'], '-o', names['png']],
+             cwd=dirname(base), stdout=PIPE, stderr=PIPE)
+
+    remove(names['log'])
+    remove(names['aux'])
+
+    if not outfile:
+        o = file(output).read()
+        remove(output)
+        return o
+    else:
+        rename(output, outfile)
+
+
+def pdflatex(file, type='pdf'):
+    call([settings.PDFLATEX_PATH, '-interaction=nonstopmode',
+          '-output-format', type, file],
+         cwd=dirname(file), stdout=PIPE, stderr=PIPE)

+ 23 - 0
coin/billing/templates/billing/invoice.tex

@@ -0,0 +1,23 @@
+{% autoescape off %}
+	%Preambule du document :
+	\documentclass[11pt]{article}
+	\usepackage[utf8]{inputenc}
+	\usepackage[francais]{babel}
+
+	%Corps du document :
+	\begin{document}
+		\begin{itemize}	
+	    	\item Number : {{ invoice.number }}
+	    	\item Date : {{ invoice.date }}
+	    	\item Facture du {{ invoice.period_from }} au {{ invoice.period_to }}
+	    	\item Paiement le : {{ invoice.date_due }}
+
+	    	\item Détails :
+	    	\begin{itemize}
+			    {% for detail in invoice.details.all %}
+			    	\item {{ detail.label }} - {{ detail.amount }} - {{ detail.quantity }} - {{ detail.tax }}
+			    {% endfor %}
+			\end{itemize}
+		\end{itemize}
+	\end{document}
+{% endautoescape %}

+ 11 - 0
coin/billing/templates/billing/test.tex

@@ -0,0 +1,11 @@
+{% autoescape off %}
+	%Preambule du document :
+	\documentclass[11pt]{report}
+	\usepackage[latin1]{inputenc}
+	\usepackage[francais]{babel}
+
+	%Corps du document :
+	\begin{document}
+	    Hello World ! : {{ blop }}
+	\end{document}
+{% endautoescape %}

+ 11 - 0
coin/billing/urls.py

@@ -0,0 +1,11 @@
+from django.conf.urls import patterns, url
+from django.views.generic import DetailView
+from coin.billing import views
+
+
+urlpatterns = patterns(
+    '',
+    url(r'^pdf_test$', views.pdf_test, name='pdf_test'),
+    url(r'^invoice/(?P<pk>\d+)/pdf$', views.invoice_pdf),
+
+)

+ 25 - 2
coin/billing/views.py

@@ -1,3 +1,26 @@
-from django.shortcuts import render
+# -*- coding: utf-8 -*-
+from django.http import HttpResponse
+from django.shortcuts import render, get_object_or_404
+from coin.billing.process_latex import process_latex
+from coin.billing.models import Invoice
 
-# Create your views here.
+
+def pdf_test(request):
+
+    context = {"blop": "COIN !"}
+
+    response = HttpResponse(content_type='application/pdf')
+    response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
+    response.write(process_latex('billing/test.tex', context))
+    return response
+
+
+def invoice_pdf(request, pk):
+
+    invoice = get_object_or_404(Invoice, pk=1)
+    context = {"invoice": invoice}
+
+    response = HttpResponse(content_type='application/pdf')
+    response['Content-Disposition'] = 'attachment; filename="facture.pdf"'
+    response.write(process_latex('billing/invoice.tex', context))
+    return response

+ 9 - 0
coin/offers/admin.py

@@ -3,8 +3,15 @@ from coin.offers.models import Offer, Service, OfferSubscription
 from coin.offers.offersubscription_filter import\
             OfferSubscriptionTerminationFilter,\
             OfferSubscriptionCommitmentFilter
+from coin.resources.models import IPSubnet
 import autocomplete_light
 
+
+class IPSubnetInline(admin.TabularInline):
+    model = IPSubnet
+    extra = 0
+
+
 class ServiceAdmin(admin.ModelAdmin):
     search_fields = ['name']
 
@@ -34,6 +41,8 @@ class OfferSubscriptionAdmin(admin.ModelAdmin):
              )
     form = autocomplete_light.modelform_factory(OfferSubscription)
 
+    inlines = [ IPSubnetInline ]
+
 admin.site.register(Service, ServiceAdmin)
 admin.site.register(Offer, OfferAdmin)
 admin.site.register(OfferSubscription, OfferSubscriptionAdmin)

+ 2 - 0
coin/settings.py

@@ -226,6 +226,8 @@ AUTH_LDAP_USER_FLAGS_BY_GROUP = {
 }
 
 
+PDFLATEX_PATH = "pdflatex"
+
 # Surcharge les paramètres en utilisant le fichier settings-local.py
 try:
     from settings_local import *

+ 1 - 0
coin/urls.py

@@ -16,6 +16,7 @@ urlpatterns = patterns(
     url(r'^members/login/$', 'django.contrib.auth.views.login',
         {'template_name': 'members/login.html'}),
     url(r'^members/', include('coin.members.urls', namespace='members')),
+    url(r'^billing/', include('coin.billing.urls', namespace='billing')),
 
     url(r'^admin/', include(admin.site.urls)),