Browse Source

Remove non-sens invoice billign period (period_from, period_to). But add theses fields to invoicedetail as optional. They are used to check if an invoice for a subscription has already been generated.

Fabs 11 years ago
parent
commit
3ed8e2980d

+ 2 - 2
coin/billing/admin.py

@@ -7,7 +7,7 @@ import autocomplete_light
 class InvoiceDetailInline(admin.StackedInline):
     model = InvoiceDetail
     extra = 0
-    fields = (('label', 'amount', 'quantity', 'tax', 'offer'),)
+    fields = (('label', 'amount', 'quantity', 'tax'), ('offer','period_from', 'period_to'))
 
 
 class PaymentInline(admin.StackedInline):
@@ -21,7 +21,7 @@ class InvoiceAdmin(admin.ModelAdmin):
 	list_display_links = ('number', 'date')
 	inlines = [InvoiceDetailInline, PaymentInline]
 	fields = (('number', 'date', 'status'),
-			  ('period_from', 'period_to', 'date_due'),
+			  ('date_due'),
 			  'member')
 	form = autocomplete_light.modelform_factory(Invoice)
 

+ 31 - 19
coin/billing/generate_invoices.py

@@ -21,7 +21,7 @@ def generate_missing_invoices(request):
 	sortie = ""
 	for member in members:
 		sortie += '<br /> %s - %s' % (member, 
-			generate_invoice_for_a_period(member, datetime.date(2013,12,5)))
+			generate_invoice_for_a_period(member, datetime.date(2014,5,17)))
 
 	return HttpResponse(sortie)
 
@@ -30,43 +30,55 @@ def generate_invoice_for_a_period(member, date):
 	"""
 	Génère si necessaire une facture pour un membre en prenant la date passée 
 	en paramètre comme premier mois de période. Renvoi la facture générée
-	ou Non si aucune facture n'était necessaire.
+	ou None si aucune facture n'était necessaire.
 	"""
 	invoice = None
 
+	# Récupère les abonnements en cours du membre
+	offer_subscriptions = (
+		OfferSubscription.get_member_offer_subscriptions(member,date))
 
-	offer_subscriptions = OfferSubscription.objects.filter(
-		Q(resign_date__isnull=True) | Q(resign_date__gte=datetime.date.today()),
-		member__exact=member.pk)
-
+	#Pour chaque abonnement
 	for offer_subscription in offer_subscriptions:
+		#Récupère l'offre de l'abonnement
 		offer = offer_subscription.offer
 
+		# Recherche les factures déjà existantes de ce membre ayant cette offre
+		# comme item pour lesquels la période de facturation englobe la date
 		invoice_test = Invoice.objects.filter(
-			period_from__lte=date,
-			period_to__gte=date,
+			# period_from__lte=date,
+			# period_to__gte=date,
 			details__offer__exact=offer.pk,
+			details__period_from__lte=date,
+			details__period_to__gte=date,
 			member__exact=member.pk)
 
-		if not invoice_test.exists():			
+		#Si une telle facture n'existe pas
+		if not invoice_test.exists():
+			#Si l'object facture n'a pas encore été créé, le créé
 			if invoice == None:
 				invoice = Invoice.objects.create(
-					period_from=datetime.date(date.year,date.month,1),
-					period_to=datetime.date(date.year,date.month,1),
+					# period_from=datetime.date(date.year,date.month,1),
+					# period_to=datetime.date(date.year,date.month,1),
 					date_due=datetime.date.today(),
 					member=member
 				)
 
-			new_period_to = (datetime.date(date.year,date.month,1) +
-							relativedelta(months = +offer.billing_period) -
-							relativedelta(days = +1))
-			if new_period_to > invoice.period_to:
-				invoice.period_to=new_period_to
-			
+			# new_period_to = (datetime.date(date.year,date.month,1) +
+			# 				relativedelta(months = +offer.billing_period) -
+			# 				relativedelta(days = +1))
+			# if new_period_to > invoice.period_to:
+			# 	invoice.period_to=new_period_to
+
+			#Ajout l'item de l'offre correspondant à l'abonnement à la facture			
 			invoice.details.create(label=offer.name,
 				amount=offer.period_fees,
-				offer=offer)
-			print invoice.period_to
+				offer=offer,
+				period_from=datetime.date(date.year,date.month,1),
+				period_to=(datetime.date(date.year,date.month,1) +
+							relativedelta(months = +offer.billing_period) -
+							relativedelta(days = +1)))
+
 			invoice.save();
 
 	return invoice

+ 117 - 0
coin/billing/migrations/0009_auto__add_field_invoicedetail_period_from__add_field_invoicedetail_per.py

@@ -0,0 +1,117 @@
+# -*- 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 'InvoiceDetail.period_from'
+        db.add_column(u'billing_invoicedetail', 'period_from',
+                      self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2014, 2, 1, 0, 0), null=True, blank=True),
+                      keep_default=False)
+
+        # Adding field 'InvoiceDetail.period_to'
+        db.add_column(u'billing_invoicedetail', 'period_to',
+                      self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2014, 2, 28, 0, 0), null=True, blank=True),
+                      keep_default=False)
+
+        # Deleting field 'Invoice.period_from'
+        db.delete_column(u'billing_invoice', 'period_from')
+
+        # Deleting field 'Invoice.period_to'
+        db.delete_column(u'billing_invoice', 'period_to')
+
+        # Adding unique constraint on 'Invoice', fields ['number']
+        db.create_unique(u'billing_invoice', ['number'])
+
+
+    def backwards(self, orm):
+        # Removing unique constraint on 'Invoice', fields ['number']
+        db.delete_unique(u'billing_invoice', ['number'])
+
+        # Deleting field 'InvoiceDetail.period_from'
+        db.delete_column(u'billing_invoicedetail', 'period_from')
+
+        # Deleting field 'InvoiceDetail.period_to'
+        db.delete_column(u'billing_invoicedetail', 'period_to')
+
+        # Adding field 'Invoice.period_from'
+        db.add_column(u'billing_invoice', 'period_from',
+                      self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2014, 2, 1, 0, 0), null=True),
+                      keep_default=False)
+
+        # Adding field 'Invoice.period_to'
+        db.add_column(u'billing_invoice', 'period_to',
+                      self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2014, 2, 28, 0, 0), null=True),
+                      keep_default=False)
+
+
+    models = {
+        u'billing.invoice': {
+            'Meta': {'object_name': 'Invoice'},
+            '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', [], {'default': "u'201402-545-398'", 'unique': 'True', 'max_length': '25'}),
+            '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'}),
+            'period_from': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 1, 0, 0)', 'null': 'True', 'blank': 'True'}),
+            'period_to': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 2, 28, 0, 0)', 'null': 'True', 'blank': 'True'}),
+            'quantity': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
+            'tax': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'null': 'True', 'max_digits': '4', 'decimal_places': '2'})
+        },
+        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', [], {'default': "'transfer'", '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']

+ 16 - 12
coin/billing/models.py

@@ -24,17 +24,6 @@ class Invoice(models.Model):
                               default='open',
                               verbose_name='Statut')
     date = models.DateField(default=datetime.date.today, null=True)
-    period_from = models.DateField(
-        default=datetime.date(datetime.date.today().year,
-                              datetime.date.today().month, 1),
-        null=True,
-        verbose_name=u'Début de période de facturation')
-    period_to = models.DateField(
-        default=(datetime.date(datetime.date.today().year,
-                               datetime.date.today().month + 1, 1) -
-                 datetime.timedelta(days=1)),
-        null=True,
-        verbose_name=u'Fin de période de facturation')
     date_due = models.DateField(
         default=(datetime.date(datetime.date.today().year,
                                datetime.date.today().month + 1, 1) -
@@ -49,7 +38,7 @@ class Invoice(models.Model):
       total = Decimal('0.0')
       for detail in self.details.all():
         total += detail.amount * (detail.tax / Decimal('100.0') +
-                                  Decimal('1.0'))
+                                  Decimal('1.0')) * detail.quantity
       return total.quantize(Decimal('0.01'))
 
     @staticmethod
@@ -81,6 +70,21 @@ class InvoiceDetail(models.Model):
                                 related_name='details')
     offer = models.ForeignKey(Offer, null=True, blank=True, default=None,
                               verbose_name='Offre')
+    period_from = models.DateField(
+        default=datetime.date(datetime.date.today().year,
+                              datetime.date.today().month, 1),
+        null=True,
+        blank=True,
+        verbose_name=u'Début de période',
+        help_text=u'Date de début de période sur laquelle est facturé cet item')
+    period_to = models.DateField(
+        default=(datetime.date(datetime.date.today().year,
+                               datetime.date.today().month + 1, 1) -
+                 datetime.timedelta(days=1)),
+        null=True,
+        blank=True,
+        verbose_name=u'Fin de période',
+        help_text=u'Date de fin de période sur laquelle est facturé cet item')
 
     def __unicode__(self):
         return self.label

+ 2 - 27
coin/billing/templates/billing/facture.tex

@@ -1,31 +1,6 @@
+{% autoescape off %}
 \documentclass{facture}
 \usepackage{lipsum}
-\setmainfont[Mapping=tex-text]{Palatino}
-\datelimite{30}
-\dest{{{member.first_name}} {{member.last_name}}\\
-{{member.address}}\\
-{{member.postal_code}} {member.city}} 
-}
-\fact{{{member.first_name}} {{member.last_name}}}
-{{member.address}}\\
-{{member.postal_code}} {{member.city}} 
-}
-\nomemet{Illyse - chez Jean-François Mourgues}
-\adresseemet{225 route de Genas\\
-69100 Villeurbanne\\
-contact@illyse.org \\
-}
-
-\numero{invoice.number}
-\type{Facture}
-\pied{Payable à l’ordre de Illyse}
-\begin{document}
-\entete
-
-\begin{facture}
-{% for detail in InvoiceDetail }
-\ligne{detail.label}[detail.quantity]{detail.amount}[][detail.tax]
-{% endfor %}
-\end{facture}
 
 \end{document}
+{% endautoescape %}

+ 4 - 3
coin/billing/views.py

@@ -17,10 +17,11 @@ def pdf_test(request):
 
 def invoice_pdf(request, pk):
 
-    invoice = get_object_or_404(Invoice, pk=1)
-    context = {"invoice": invoice}
+    invoice = get_object_or_404(Invoice, pk=pk)
+    member = invoice.member
+    context = {"invoice": invoice, 'member':member}
 
     response = HttpResponse(content_type='application/pdf')
     response['Content-Disposition'] = 'attachment; filename="facture.pdf"'
-    response.write(process_latex('billing/invoice.tex', context))
+    response.write(process_latex('billing/facture.tex', context))
     return response

+ 8 - 0
coin/offers/models.py

@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 import datetime
 from django.db import models
+from django.db.models import Q
 
 
 class Service(models.Model):
@@ -57,5 +58,12 @@ class OfferSubscription(models.Model):
         return u'%s - %s - %s' % (self.member, self.offer.name,
                                    self.subscription_date)
 
+    @staticmethod
+    def get_member_offer_subscriptions(member, date=datetime.date.today()):
+      return OfferSubscription.objects.filter(
+        Q(member__exact=member.pk),
+        Q(subscription_date__lte=date),
+        Q(resign_date__isnull=True) | Q(resign_date__gte=date))
+
     class Meta:
         verbose_name = 'abonnement'