Source code for creditcards.models

from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone

from core.core import _american_format
from core.models import AccountWrapper
from entries.models import JournalEntry, Transaction
from receipts.models import Receipt


[docs]class CreditCard(AccountWrapper): """A Communard-Visible Wrapper for an :class:`~accounts.models.Account`. .. attribute:: account The :class:`~accounts.models.Account` the CreditCard is linked to. This is the Account that will be credited when CreditCardEntries are approved. .. attribute:: name A name for the Credit Card, used in forms accessible by Communards. """
[docs]class CreditCardEntry(models.Model): """Communard entries used as a pre-approval state for JournalEntries. These are used to group together :class:`CreditCardTransactions <CreditCardTransaction>` before the Entry is approved by an Accountant. When approved, the CreditCardEntry massages it's data into a JournalEntry and Transactions so that the relevant Account balances are modified, then deletes itself. .. attribute:: date The date the entry occured. .. attribute:: card The CreditCard the entry belongs too. .. attribute:: name The name of the communard who submitted the entry. .. attribute:: merchant The name of the merchant the purchase was made at. .. attribute:: amount The total amount of money spent. This is balanced against related CreditCardTransactions. .. attribute:: comments Additional comments from the Communard. .. attribute:: created_at The date & time the Entry was created. """ date = models.DateField() card = models.ForeignKey(CreditCard, verbose_name="Credit Card") name = models.CharField(max_length=60) merchant = models.CharField(max_length=60) amount = models.DecimalField( help_text="Positive balance is a charge, negative is a return.", verbose_name="Total Amount", max_digits=19, decimal_places=4) comments = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True, default=timezone.now) class Meta(object): ordering = ('card', 'date', 'created_at',) def __unicode__(self): return '{} - {} - {}'.format( _american_format(self.date), self.name, self.merchant) def get_absolute_url(self): return reverse('creditcards.views.show_creditcard_entry', args=[str(self.id)])
[docs] def generate_memo(self): """Create a memo line from the Entry's attributes.""" return "{} at {} by {}".format( self.card.name, self.merchant, self.name)
[docs] def get_number(self): """Generate an Entry number using the id.""" return "CC#{0:06d}".format(self.id)
[docs] def get_edit_url(self): """Return an edit link to the Entry's edit page.""" return reverse('creditcards.views.add_creditcard_entry', args=[str(self.id)])
[docs] def get_next_entry(self): """Return a Queryset of the next possible Entries to display.""" return CreditCardEntry.objects.filter( card=self.card, date__gte=self.date ).exclude(pk=self.pk).order_by('date', 'id')
[docs] def approve_entry(self): """Creating a JournalEntry Transactions and Receipts from the Entry. This does not delete the entry, as should be done when an Entry is approved. You **must manually delete** the CreditCardEntry. Returns the created JournalEntry. """ journal_entry = JournalEntry.objects.create( date=self.date, memo=self.generate_memo(), comments=self.comments) transactions = self.transaction_set.all() if transactions.count() == 1: creditcard_detail = transactions[0].detail else: creditcard_detail = 'Purchases by {}'.format(self.name) for transaction in transactions: Transaction.objects.create( journal_entry=journal_entry, account=transaction.account, detail=transaction.detail, balance_delta=(-1 * transaction.amount) ) Transaction.objects.create( journal_entry=journal_entry, account=self.card.account, balance_delta=self.amount, detail=creditcard_detail, ) for receipt in self.receipt_set.all(): new_receipt = ContentFile(receipt.receipt_file.file.read()) new_receipt.name = receipt.receipt_file.name Receipt.objects.create( journal_entry=journal_entry, receipt_file=new_receipt) return journal_entry
[docs]class CreditCardTransaction(models.Model): """Represents the individual charges for a :class:`CreditCardEntry`. Unlike a :class:`entries.models.Transaction`, a CreditCardTransaction does not affect the balance of it's :class:`entries.models.Account`. .. attribute:: creditcard_entry The :class:`CreditCardEntry` the CreditCardTransaction belongs to .. attribute:: account The related :class:`accounts.models.Account`. .. attribute:: detail Information about the specific charge. .. attribute:: balance_delta The change in balance this :class:`Transaction` represents. A positive value indicates a Credit while a negative value is a Debit. """ creditcard_entry = models.ForeignKey( CreditCardEntry, related_name='transaction_set') account = models.ForeignKey( 'accounts.Account', on_delete=models.PROTECT) detail = models.CharField( help_text="Short description of the charge", blank=True, max_length=50) amount = models.DecimalField( help_text="Positive value is a charge, negative is a return", verbose_name="Item Amount", max_digits=19, decimal_places=4) class Meta(object): ordering = ['id'] def __unicode__(self): return "{} - {}".format(self.creditcard_entry.name, self.detail)
[docs]class CreditCardReceipt(models.Model): """Stores Receipts for an unapproved :class:`CreditCardEntry`. When the CreditCardEntry is approved, these are turned into :class:`receipts.models.Receipts`. .. attribute:: receipt_file The actual receipt stored as a file. .. attribute:: creditcard_entry The :class:`CreditCardEntry` this receipt bleongs to. """ creditcard_entry = models.ForeignKey( CreditCardEntry, related_name='receipt_set') receipt_file = models.FileField( blank=True, null=True, upload_to='uploads/unapproved-cc-receipts/')