from django.db import models, connection import settings import treebeard.mp_tree import datetime class BudgetArea(treebeard.mp_tree.MP_Node): indent_str = u"\u00A0\u00A0" name = models.CharField(max_length=50) comment = models.TextField(blank=True) # Applicable to every term? always = models.BooleanField(blank=True, default=False) budget_term = models.ManyToManyField('BudgetTerm', through='BudgetAreaTerm') # Contact / ACL information # If not specified, inherit from parent owner = models.EmailField(help_text = 'Email address of the officer responsible for the area', blank=True) # owner of the budget area interested = models.EmailField(help_text='Email address of parties interested in the area', blank=True) # interested parties (ie, whole committee) use_owner = models.BooleanField(default=False, blank=True) account_number = models.IntegerField(help_text='Account number: for example, cost object or GL', blank=True, null=True) def contact_address(self,): address = '' if self.use_owner: address = self.owner or self.interested else: address = self.interested or self.owner def owner_address(self,): address = self.owner if address: return address else: parent = self.get_parent() if parent: return self.get_parent().owner_address() else: return settings.ADMINS[0][1] def get_account_number(self): """Retrieve the account number for this account. This properly recurses through the hierarchy until reaching the root or an account with the account number set.""" print self.account_number if self.account_number: return self.account_number else: parent = self.get_parent() if parent: return parent.get_account_number() else: return 0 def mark_used(self, term, comment=""): BudgetAreaTerm.objects.get_or_create( budget_area=self, budget_term=term, defaults={'comment':comment} ) @classmethod def get_by_path(cls, path, base=None, ): if base: node = base else: try: root = BudgetArea.objects.get(name=path[0], depth=1) except IndexError, e: raise KeyError(e) node = root path = path[1:] for name in path: try: node = node.get_children().filter(name=name)[0] except IndexError, e: raise KeyError(e) return node @classmethod def get_by_pathstr(cls, path, base=None): path = path.split('.') return cls.get_by_path(path, base=base, ) def pathstr(self, skip=0): if self.depth-1 > skip: parent = self.get_parent() prefix = parent.pathstr(skip=skip) + '.' else: prefix = '' return prefix + self.name def dump_to_html(self): struct = self.dump_bulk() return self.struct_to_html(struct, depth=0) def struct_to_html(self, struct, depth=0): def format_data(data): return ""+data['name']+" "+unicode(data) prefix = "\t"*depth html = prefix+"\n" return html def indented_name(self, strip_levels=0): return self.indent_str*(self.depth-strip_levels) + unicode(self) def __unicode__(self,): return u"%s [%s] (%s)" % (self.name, self.contact_address(), self.always, ) class Meta: permissions = ( ('use_reporting', 'Can use basic reporting functionality',), ) ordering = ['path'] class BudgetTerm(models.Model): name = models.CharField(max_length=20) slug = models.SlugField(max_length=20) start_date = models.DateField() end_date = models.DateField() submit_deadline = models.DateField() def __unicode__(self,): return "%s (%s to %s [due %s])" % (self.name, self.start_date, self.end_date, self.submit_deadline, ) class BudgetAreaTerm(models.Model): budget_area = models.ForeignKey(BudgetArea) budget_term = models.ForeignKey(BudgetTerm) comment = models.TextField(blank=True, ) def __unicode__(self,): return "%s during %s" % (self.budget_area, self.budget_term, ) class Transaction(models.Model): name = models.CharField(max_length=60) desc = models.TextField(blank=True) incurred_time = models.DateTimeField(default=datetime.datetime.now, help_text='Time the item or service was purchased') tx_create_time = models.DateTimeField(default=datetime.datetime.now) def __unicode__(self,): return self.name def make_transfer(name, amount, layer, budget_term, from_area, to_area, desc, incurred_time, ): tx = Transaction( name=name, desc=desc, incurred_time=incurred_time, ) tx.save() from_li = LineItem( label='Send: %s' % (name, ), amount=-amount, budget_area=from_area, budget_term=budget_term, layer=layer, tx=tx, ) from_li.save() to_li = LineItem( label='Receive: %s' % (name, ), amount=amount, budget_area=to_area, budget_term=budget_term, layer=layer, tx=tx, ) to_li.save() return tx class LineItem(models.Model): tx = models.ForeignKey(Transaction) amount = models.DecimalField(max_digits=7, decimal_places=2, help_text='Do not include "$"') label = models.CharField(max_length=60) budget_area = models.ForeignKey(BudgetArea) budget_term = models.ForeignKey(BudgetTerm) layer = models.IntegerField() # this might actually be a Transaction property... def layer_string(self,): layer = self.layer return layer_name(get_layer_by_num(layer)) def __unicode__(self, ): return "%s: %s: $%s (%s) in %s during %s" % ( self.tx, self.label, self.amount, self.layer, self.budget_area, self.budget_term, ) LAYER_BUDGET = 10 LAYER_ALLOCATION = 20 LAYER_EXPENDITURE = 30 LAYER_CLOSEOUT = 40 layers=( # Guessing the meanings here a bit... (LAYER_BUDGET, 'budget'), # requested amount (LAYER_ALLOCATION, 'allocation'), # amount approved (LAYER_EXPENDITURE, 'expenditure'), # spent amount (LAYER_CLOSEOUT, 'closeout'), # difference between spent and allocated # Remit was originally developed for the UA, so I'm guessing the layers # reflect a process where the treasurer proposes a budget, the body # approves it (potentially with amendments), committees spend it, and then # you balance the accounts at the end ) def get_layer_by_name(name): for layer in layers: if name == layer[1]: return layer raise KeyError, "layer %s not found" % (name, ) def get_layer_by_num(num): for layer in layers: if num == layer[0]: return layer raise KeyError, "layer %d not found" % (num, ) def layer_name(layer): return layer[1] def layer_num(layer): return layer[0] def coerce_full_email(email): if '@' in email: return email else: return email + '@' + settings.DEFAULT_DOMAIN