lincolnloop / django-beancounter
- Source
- Commits
- Network (2)
- Issues (0)
- Downloads (0)
- Wiki (1)
- Graphs
-
Tree:
f9ffe2c
django-beancounter / beancounter / models.py
| 9d6640ef » | lincolnloop | 2009-01-04 | 1 | import datetime | |
| 1c4c5ca6 » | lincolnloop | 2009-01-04 | 2 | import decimal | |
| 9d6640ef » | lincolnloop | 2009-01-04 | 3 | ||
| ca5ea477 » | lincolnloop | 2008-08-04 | 4 | from django.db import models | |
| b13309e1 » | lincolnloop | 2008-12-09 | 5 | from django.contrib.localflavor.us.models import PhoneNumberField | |
| 9d6640ef » | lincolnloop | 2009-01-04 | 6 | from tagging.fields import TagField | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 7 | ||
| 8 | TYPE_CHOICES = ( | ||||
| 9 | ('INC', 'Income'), | ||||
| 10 | ('EXP', 'Expense'), | ||||
| 11 | ('COGS', 'Cost of Goods Sold'), | ||||
| 12 | ) | ||||
| 13 | class Category(models.Model): | ||||
| 4b8ad771 » | lincolnloop | 2008-08-04 | 14 | type = models.CharField(max_length=4,choices=TYPE_CHOICES) | |
| 15 | name = models.CharField(max_length=50) | ||||
| ca5ea477 » | lincolnloop | 2008-08-04 | 16 | income = models.ForeignKey('self', null=True, blank=True, limit_choices_to = {'type__exact':'INC'}, help_text='Use this to enable tracking your costs of goods vs. income') | |
| 17 | |||||
| 18 | def __str__(self): | ||||
| 19 | return "%s: %s" % (self.type,self.name) | ||||
| 20 | class Meta: | ||||
| 21 | verbose_name_plural = 'Categories' | ||||
| 22 | ordering = ['type','name'] | ||||
| 23 | |||||
| 24 | class Admin: | ||||
| 25 | fields = ( | ||||
| 26 | (None, { | ||||
| 27 | 'fields': ('type','name') | ||||
| 28 | }), | ||||
| 29 | ('Associate Costs of Goods Sold to an Income Category', { | ||||
| 30 | 'classes': 'collapse', | ||||
| 31 | 'fields' : ('income',) | ||||
| 32 | }), | ||||
| 33 | ) | ||||
| 34 | |||||
| 35 | class BankAccount(models.Model): | ||||
| 4b8ad771 » | lincolnloop | 2008-08-04 | 36 | type = models.CharField(max_length=50,help_text='Checking, Savings, Credit, etc.') | |
| 37 | name = models.CharField(max_length=50) | ||||
| bf69f37a » | lincolnloop | 2008-08-19 | 38 | initial_balance = models.DecimalField(max_digits=10, decimal_places=2, null=True,blank=True) | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 39 | track_balance = models.BooleanField(help_text='Generate reports of the balance of this account over time.') | |
| 40 | |||||
| 41 | def __str__(self): | ||||
| 42 | return "%s (%s)" % (self.name,self.type) | ||||
| 43 | class Admin: | ||||
| 44 | list_display = ('name','type') | ||||
| 45 | ordering = ['-track_balance','name','type'] | ||||
| 46 | |||||
| 47 | class AccountTransfer(models.Model): | ||||
| 48 | date = models.DateField() | ||||
| 49 | from_account = models.ForeignKey(BankAccount,related_name='transferred_from') | ||||
| 50 | to_account = models.ForeignKey(BankAccount,related_name='transferred_to') | ||||
| bf69f37a » | lincolnloop | 2008-08-19 | 51 | amount = models.DecimalField(max_digits=8, decimal_places=2) | |
| 4b8ad771 » | lincolnloop | 2008-08-04 | 52 | memo = models.CharField(max_length=100,null=True,blank=True) | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 53 | def __str__(self): | |
| 54 | return "$%.2f from %s to %s" % (self.amount,self.from_account,self.to_account) | ||||
| 55 | |||||
| 56 | class Admin: | ||||
| 57 | list_display = ('date','amount','from_account','to_account') | ||||
| 58 | list_filter = ('to_account','from_account') | ||||
| 59 | date_hierarchy = 'date' | ||||
| 60 | |||||
| 61 | |||||
| 62 | class Person(models.Model): | ||||
| 4b8ad771 » | lincolnloop | 2008-08-04 | 63 | name = models.CharField(max_length=100) | |
| 64 | contact = models.CharField(max_length=100,null=True,blank=True) | ||||
| b13309e1 » | lincolnloop | 2008-12-09 | 65 | phone = PhoneNumberField(null=True,blank=True) | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 66 | website = models.URLField(null=True,blank=True) | |
| 67 | email = models.EmailField(null=True,blank=True) | ||||
| 4b8ad771 » | lincolnloop | 2008-08-04 | 68 | notes = models.CharField(max_length=100,null=True,blank=True) | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 69 | ||
| 9d6640ef » | lincolnloop | 2009-01-04 | 70 | def __unicode__(self): | |
| 71 | return self.name | ||||
| ca5ea477 » | lincolnloop | 2008-08-04 | 72 | ||
| 73 | class Meta: | ||||
| 74 | verbose_name_plural = 'People' | ||||
| 75 | ordering = ['name'] | ||||
| 76 | |||||
| 9d6640ef » | lincolnloop | 2009-01-04 | 77 | ||
| 78 | class Employee(Person): | ||||
| 79 | PAYMENT_CHOICES = ( | ||||
| 80 | ('paypal', 'PayPal'), | ||||
| 81 | ('check', 'Mail Check'), | ||||
| 82 | ('wire', 'Wire Transfer'), | ||||
| 83 | ('elance', 'Elance'), | ||||
| 84 | ('other', 'Other'), | ||||
| 85 | ) | ||||
| e4ceba0f » | lincolnloop | 2009-01-04 | 86 | gmt_offset = models.DecimalField(max_digits=3, decimal_places=1, null=True, blank=True) | |
| 9d6640ef » | lincolnloop | 2009-01-04 | 87 | skills = TagField() | |
| 88 | payment_preference = models.CharField(blank=True, max_length=100, choices=PAYMENT_CHOICES) | ||||
| 89 | payment_notes = models.TextField(blank=True) | ||||
| 90 | contract = models.DateField(blank=True, null=True, help_text="Date contractor contract was signed and received.") | ||||
| 91 | hourly_rate = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True, help_text="If rate varies, enter average and note below.") | ||||
| 92 | currency = models.CharField(default="USD", max_length=3) | ||||
| 93 | rate_notes = models.TextField(blank=True, help_text="Additional notes regarding contractor rates.") | ||||
| 94 | |||||
| 95 | |||||
| 96 | def timezone(self): | ||||
| 97 | if self.gmt_offset == int(self.gmt_offset): | ||||
| 98 | gmt_offset = int(self.gmt_offset) | ||||
| 99 | else: | ||||
| 100 | gmt_offset = self.gmt_offset | ||||
| 101 | if gmt_offset > 0: | ||||
| 102 | gmt_offset = '+%s' % gmt_offset | ||||
| 103 | return 'GMT%s' % gmt_offset | ||||
| 104 | |||||
| 105 | def under_contract(self): | ||||
| 106 | if self.contract: | ||||
| 107 | return True | ||||
| 108 | return False | ||||
| 109 | under_contract.boolean = True | ||||
| 110 | |||||
| 111 | def rate(self): | ||||
| 112 | return "%s %s" % (self.hourly_rate, self.currency) | ||||
| 113 | |||||
| 114 | class Project(models.Model): | ||||
| 115 | name = models.CharField(max_length=100) | ||||
| 116 | employees = models.ManyToManyField(Employee) | ||||
| 117 | active = models.BooleanField(default=True) | ||||
| 118 | |||||
| 119 | def __unicode__(self): | ||||
| 120 | return self.name | ||||
| 1c4c5ca6 » | lincolnloop | 2009-01-04 | 121 | ||
| 122 | def total_invoiced(self): | ||||
| 123 | #TODO aggregate support | ||||
| 124 | total = decimal.Decimal("0.00") | ||||
| 125 | for invoice in self.projectinvoice_set.all(): | ||||
| 126 | total += invoice.amount | ||||
| 127 | return total | ||||
| 128 | |||||
| 129 | def total_cost(self): | ||||
| 130 | #TODO aggregate support | ||||
| 131 | total = decimal.Decimal("0.00") | ||||
| 132 | for time in self.projecttime_set.all(): | ||||
| 133 | total += time.cost_converted | ||||
| 134 | return total | ||||
| 135 | |||||
| 136 | def profit(self): | ||||
| 137 | return self.total_invoiced() - self.total_cost() | ||||
| 138 | |||||
| e4ceba0f » | lincolnloop | 2009-01-04 | 139 | ||
| 140 | class ProjectInvoice(models.Model): | ||||
| 141 | """ | ||||
| 142 | Invoice sent for project | ||||
| 143 | |||||
| 144 | """ | ||||
| 145 | project = models.ForeignKey(Project) | ||||
| 146 | date = models.DateField(default=datetime.date.today()) | ||||
| 147 | amount = models.DecimalField(max_digits=8, decimal_places=2) | ||||
| 148 | |||||
| 149 | def __unicode__(self): | ||||
| 150 | return "$%s for %s on %s" % (self.amount, self.project, self.date) | ||||
| 9d6640ef » | lincolnloop | 2009-01-04 | 151 | ||
| 152 | |||||
| 153 | class ProjectTime(models.Model): | ||||
| 154 | """ | ||||
| 155 | Hours spent by an employee on a project | ||||
| ca5ea477 » | lincolnloop | 2008-08-04 | 156 | ||
| 9d6640ef » | lincolnloop | 2009-01-04 | 157 | """ | |
| 158 | |||||
| 159 | employee = models.ForeignKey(Employee) | ||||
| 160 | project = models.ForeignKey(Project) | ||||
| 161 | start_date = models.DateField(default=datetime.date.today()) | ||||
| 162 | end_date = models.DateField(default=datetime.date.today()) | ||||
| 163 | hours = models.DecimalField(max_digits=6, decimal_places=3) | ||||
| 164 | cost = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True, help_text="Leave blank to automatically calculate") | ||||
| 165 | cost_converted = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True, help_text="Cost converted to local currency") | ||||
| 166 | |||||
| 167 | def __unicode__(self): | ||||
| 168 | return "%s on %s (%s-%s)" % (self.employee, self.project, self.start_date, self.end_date) | ||||
| f9ffe2cd » | lincolnloop | 2009-01-05 | 169 | ||
| 170 | def save(self, force_insert=False, force_update=False): | ||||
| 171 | if not self.cost: | ||||
| 172 | self.cost = self.hours * self.employee.hourly_rate | ||||
| 173 | if not self.cost_converted and self.employee.currency == "USD": | ||||
| 174 | self.cost_converted = self.cost | ||||
| 175 | super(ProjectTime, self).save(force_insert, force_update) | ||||
| 9d6640ef » | lincolnloop | 2009-01-04 | 176 | ||
| ca5ea477 » | lincolnloop | 2008-08-04 | 177 | ||
| 178 | class Entry(models.Model): | ||||
| 179 | category = models.ForeignKey(Category) | ||||
| 180 | date = models.DateField() | ||||
| 181 | name = models.ForeignKey(Person) | ||||
| 82d729a8 » | lincolnloop | 2008-08-05 | 182 | amount = models.DecimalField(max_digits=8, decimal_places=2) | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 183 | bank_account = models.ForeignKey(BankAccount,related_name='paid_from',null=True,blank=True) | |
| 4b8ad771 » | lincolnloop | 2008-08-04 | 184 | memo = models.CharField(max_length=100,null=True,blank=True) | |
| ca5ea477 » | lincolnloop | 2008-08-04 | 185 | ||
| 186 | def __str__(self): | ||||
| 187 | return "$%.2f | %s | %s" % (self.amount,self.name,self.date) | ||||
| 188 | class Meta: | ||||
| 189 | verbose_name_plural = 'Entries' | ||||
| 190 | |||||
| 191 | class Admin: | ||||
| 192 | list_display = ('date', 'name', 'category', 'amount') | ||||
| 193 | date_hierarchy = 'date' | ||||
| 194 | search_fields = ('name','memo') | ||||
| 195 | list_filter = ('category','name','bank_account') | ||||
