Hello,

here is a code example that illustrates how I plan to implement journals
in an accounting application. The attached file models.py contains the
details. I saved it (together with an empty __init__.py) in a new
directory "journals" under django/tests/modeltests of my SVN working
copy (which I just updated to revision 10921), then I went to
django/tests and run the command::

  python runtests.py --settings=settings_memory -v1 journals

to test it. I'm almost happy, but I get an unexpected result in line 60::

  File "L:\snapshot\django\tests\modeltests\journals\models.py", \
     line 60, in modeltests.journals.models
  Failed example:
      INV.lastnum
  Expected:
      1
  Got:
      2

I don't understand why INV.lastnum returns 2 and not 1 at this place. If
you see the reason, then please tell me!

Also, I'd be glad to get feedback on my way of solving this particular
problem, and I suggest to add this to the Django tests collection once I
got it working.

Luc

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

"""
Author: Luc Saffre 2009-06-04

A journal is a sequence of numbered documents in accounting applications.
A Journal instance knows the model used for documents in this journal.
A Document instance can look at its journal to find out which subclass 
of Document it is.
The journal also manages a counter "lastnum" to control the numbering
of documents. 

First we create two journals and some documents::

  >>> ORD = Journal(id="ORD",name="Orders",doctype=0)
  >>> ORD.save()
  >>> INV = Journal(id="INV",name="Invoices",doctype=1,force_sequence=True)
  >>> INV.save()
  >>> ORD.create_document().save()
  >>> INV.create_document().save()
  >>> ORD.create_document().save()
  >>> ORD.create_document().save()
  >>> INV.create_document().save()
  >>> for doc in Document.objects.all():
  ...    print unicode(doc)
  ORD#1 (1)
  INV#1 (2)
  ORD#2 (3)
  ORD#3 (4)
  INV#2 (5)

You can delete any document, leaving a hole in the sequence, 
and this number will not be reused.

  >>> ORD.lastnum
  3
  >>> Document.objects.get(pk=1).delete()
  Deleting ORD#1 (1)
  >>> ORD.lastnum
  3
  >>> doc = ORD.create_document()
  >>> doc.save()
  >>> print doc
  ORD#4 (6)

For invoices, ``force_sequence=True`` means that it is not allowed to
delete documents in the middle because this would leave a hole in the
sequence::

  >>> Document.objects.get(pk=2).delete()
  Traceback (most recent call last):
  ...
  DocumentError: INV#1 (2) is not the last document in journal

It's okay to delete the last invoice of a journal. 
This will also decrement lastnum so that the number will be reused::

  >>> INV.lastnum
  2
  >>> doc = Document.objects.get(pk=5)
  >>> print doc
  INV#2 (5)
  >>> doc.delete()
  Deleting INV#2 (5)
  >>> INV.lastnum
  1
  >>> doc = INV.create_document()
  >>> doc.save()
  >>> print doc
  INV#2 (7)

Here is again the list of documents in the database after these
operations::

  >>> for doc in Document.objects.all():
  ...    print unicode(doc)
  INV#1 (2)
  ORD#2 (3)
  ORD#3 (4)
  ORD#4 (6)
  INV#2 (7)

"""

from django.db import models

class DocumentError(Exception):
  pass
  
DOCTYPE_CLASSES = []
DOCTYPE_CHOICES = []

def register_doctype(cl):
    n = len(DOCTYPE_CHOICES)
    DOCTYPE_CHOICES.append((n,cl.__name__))
    DOCTYPE_CLASSES.append(cl)

class Journal(models.Model):

    id = models.CharField(max_length=4,primary_key=True)
    name = models.CharField(max_length=100)
    doctype = models.IntegerField(choices=DOCTYPE_CHOICES)
    lastnum = models.IntegerField(blank=True,null=True,default=0)
    force_sequence = models.BooleanField(default=False)
    
    def create_document(self,**kw):
        cl = DOCTYPE_CLASSES[self.doctype]
        if not kw.has_key('number'):
            kw['number'] = self.lastnum + 1
        doc = cl(journal=self,**kw)
        self.lastnum = kw['number'] 
        self.save()
        return doc
        
    def on_delete_document(self,doc):
        if doc.number == self.lastnum:
            #print "foo", self.lastnum
            self.lastnum -= 1
            #print "bar", self.lastnum
            self.save()
        elif self.force_sequence:
            raise DocumentError(
              "%s is not the last document in journal" % unicode(doc)
              )
    
class Document(models.Model):
  
    number = models.IntegerField()
    journal = models.ForeignKey(Journal)
    
    def __unicode__(self):
        return "%s#%d (%d)" % (self.journal.id,self.number,self.id)
        
    def delete(self):
        self.journal.on_delete_document(self)
        print "Deleting", self
        return super(Document,self).delete()
        
class Order(Document):
    pass

class Invoice(Document):
    pass

register_doctype(Order)
register_doctype(Invoice)

Reply via email to