Browse Source

+Automatic random invoice number
+Automatic invoice amount calculation
+Move process_latex function to global coin project

Fabs 11 years ago
parent
commit
eee158002b

+ 91 - 0
coin/billing/migrations/0007_auto__del_field_invoice_amount.py

@@ -0,0 +1,91 @@
+# -*- 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 'Invoice.amount'
+        db.delete_column(u'billing_invoice', 'amount')
+
+
+    def backwards(self, orm):
+
+        # User chose to not deal with backwards NULL issues for 'Invoice.amount'
+        raise RuntimeError("Cannot reverse this migration. 'Invoice.amount' and its values cannot be restored.")
+        
+        # The following code is provided here to aid in writing a correct migration        # Adding field 'Invoice.amount'
+        db.add_column(u'billing_invoice', 'amount',
+                      self.gf('django.db.models.fields.DecimalField')(max_digits=5, decimal_places=2),
+                      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'2014-2'", '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', [], {'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']

+ 85 - 0
coin/billing/migrations/0008_auto__chg_field_invoicedetail_tax.py

@@ -0,0 +1,85 @@
+# -*- 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):
+
+        # Changing field 'InvoiceDetail.tax'
+        db.alter_column(u'billing_invoicedetail', 'tax', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=4, decimal_places=2))
+
+    def backwards(self, orm):
+
+        # Changing field 'InvoiceDetail.tax'
+        db.alter_column(u'billing_invoicedetail', 'tax', self.gf('django.db.models.fields.IntegerField')(null=True))
+
+    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-356-412'", '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', [], {'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']

+ 27 - 7
coin/billing/models.py

@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 import datetime
 import calendar
+import random
+from decimal import Decimal
 from django.db import models
 from coin.offers.models import Offer
 from coin.members.models import Member
@@ -14,13 +16,13 @@ class Invoice(models.Model):
         ('trouble', u'Litige')
     )
 
-    number = models.CharField(max_length=25)
-
+    number = models.CharField(max_length=25,
+                              default=lambda:Invoice.next_invoice_number(),
+                              unique=True,
+                              verbose_name='Numéro')
     status = models.CharField(max_length=50, choices=INVOICES_STATUS_CHOICES,
                               default='open',
                               verbose_name='Statut')
-    amount = models.DecimalField(max_digits=5, decimal_places=2,
-                                 verbose_name='Montant')
     date = models.DateField(default=datetime.date.today, null=True)
     period_from = models.DateField(
         default=datetime.date(datetime.date.today().year,
@@ -42,8 +44,23 @@ class Invoice(models.Model):
     member = models.ForeignKey(Member, null=True, blank=True, default=None,
                                verbose_name='Membre')
 
+    def amount(self):
+      "Calcul le montant de la facture en fonction des éléments de détails"
+      total = Decimal('0.0')
+      for detail in self.details.all():
+        total += detail.amount * (detail.tax / Decimal('100.0') + Decimal('1.0'))
+      return total.quantize(Decimal('0.01'))
+
+    @staticmethod
+    def next_invoice_number():
+      "Détermine un numéro de facture aléatoire"
+      return u'%s%02i-%i-%i' % (datetime.date.today().year,
+                                datetime.date.today().month,
+                                random.randrange(100, 999),
+                                random.randrange(100, 999))
+
     def __unicode__(self):
-        return u'#%s %s€ %s' % (self.number, self.amount, self.date_due)
+        return u'#%s %0.2f€ %s' % (self.number, self.amount(), self.date_due)
 
     class Meta:
         verbose_name = 'facture'
@@ -54,8 +71,11 @@ class InvoiceDetail(models.Model):
     label = models.CharField(max_length=100)
     amount = models.DecimalField(max_digits=5, decimal_places=2,
                                  verbose_name='Montant')
-    quantity = models.IntegerField(null=True, verbose_name=u'Quantité')
-    tax = models.IntegerField(null=True, default=0, verbose_name='TVA')
+    quantity = models.IntegerField(null=True, verbose_name=u'Quantité',
+                                   default=1)
+    tax = models.DecimalField(null=True, default=0.0, decimal_places=2,
+                              max_digits=4, verbose_name='TVA',
+                              help_text='en %')
     invoice = models.ForeignKey(Invoice, verbose_name='Facture',
                                 related_name='details')
     offer = models.ForeignKey(Offer, null=True, blank=True, default=None,

+ 1 - 1
coin/billing/views.py

@@ -1,7 +1,7 @@
 # -*- 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.process_latex import process_latex
 from coin.billing.models import Invoice
 
 

coin/billing/process_latex.py → coin/process_latex.py