source: remit/finance_core/models.py @ ee37f93

client
Last change on this file since ee37f93 was dd2c3d9, checked in by Alex Dehnert <adehnert@…>, 15 years ago

Allow filtering by term

  • Property mode set to 100644
File size: 6.4 KB
Line 
1from django.db import models, connection
2import settings
3import treebeard.mp_tree
4
5
6class BudgetArea(treebeard.mp_tree.MP_Node):
7    indent_str = u"\u00A0\u00A0"
8
9    name = models.CharField(max_length=50)
10    comment = models.TextField(blank=True)
11
12    # Applicable to every term?
13    always = models.BooleanField(blank=True, default=False)
14    budget_term = models.ManyToManyField('BudgetTerm', through='BudgetAreaTerm')
15
16    # Contact / ACL information
17    # If not specified, inherit from parent
18    owner = models.EmailField(help_text = 'Email address of the officer responsible for the area', blank=True) # owner of the budget area
19    interested = models.EmailField(help_text='Email address of parties interested in the area', blank=True) # interested parties (ie, whole committee)
20    use_owner = models.BooleanField(default=False, blank=True)
21    account_number = models.IntegerField(help_text='Account number: for example, cost object or GL', blank=True, null=True)
22
23    def contact_address(self,):
24        address = ''
25        if self.use_owner:
26            address = self.owner or self.interested
27        else:
28            address = self.interested or self.owner
29
30    def owner_address(self,):
31        address = self.owner
32        return address or self.get_parent().owner_address()
33
34    def get_account_number(self):
35        """Retrieve the account number for this account.
36
37        This properly recurses through the hierarchy until reaching the root
38        or an account with the account number set."""
39
40        print self.account_number
41        if self.account_number:
42            return self.account_number
43        else:
44            parent = self.get_parent()
45            if parent:
46                return parent.get_account_number()
47            else:
48                return 0
49
50    def mark_used(self, term, comment=""):
51        BudgetAreaTerm.objects.get_or_create(
52            budget_area=self,
53            budget_term=term,
54            defaults={'comment':comment}
55        )
56
57    @classmethod
58    def get_by_path(cls, path):
59        try:
60            root = BudgetArea.objects.get(name=path[0], depth=1)
61        except IndexError, e:
62            raise KeyError(e)
63        node = root
64        for name in path[1:]:
65            try:
66                node = node.get_children().filter(name=name)[0]
67            except IndexError, e:
68                raise KeyError(e)
69        return node
70
71    @classmethod
72    def get_by_pathstr(cls, path):
73        path = path.split('.')
74        return cls.get_by_path(path)
75
76    def pathstr(self, skip=0):
77        if self.depth-1 > skip:
78            parent = self.get_parent()
79            prefix = parent.pathstr(skip=skip) + '.'
80        else:
81            prefix = ''
82        return prefix + self.name
83
84    def dump_to_html(self):
85        struct = self.dump_bulk()
86        return self.struct_to_html(struct, depth=0)
87
88    def struct_to_html(self, struct, depth=0):
89        def format_data(data):
90            return "<em>"+data['name']+"</em> "+unicode(data)
91        prefix = "\t"*depth
92        html = prefix+"<ul>\n"
93        html = html + "\n".join(
94            [("%(prefix)s\t<li>%(data)s\n%(children)s\t%(prefix)s</li>\n" % {
95                'prefix':prefix,
96                'data':format_data(elem['data']),
97                'children':('children' in elem and self.struct_to_html(elem['children'], depth+1) or '')
98            }) for elem in struct])
99        html = html + prefix + "</ul>\n"
100        return html
101
102    def indented_name(self, strip_levels=0):
103        return self.indent_str*(self.depth-strip_levels) + unicode(self)
104
105    def __unicode__(self,):
106        return u"%s [%s] (%s)" % (self.name, self.contact_address(), self.always, )
107
108
109class BudgetTerm(models.Model):
110    name = models.CharField(max_length=20)
111    slug = models.SlugField(max_length=20)
112    start_date = models.DateField()
113    end_date = models.DateField()
114    submit_deadline = models.DateField()
115
116    def __unicode__(self,):
117        return "%s (%s to %s [due %s])" % (self.name, self.start_date, self.end_date, self.submit_deadline, )
118
119
120class BudgetAreaTerm(models.Model):
121    budget_area = models.ForeignKey(BudgetArea)
122    budget_term = models.ForeignKey(BudgetTerm)
123    comment = models.TextField(blank=True, )
124
125    def __unicode__(self,):
126        return "%s during %s" % (self.budget_area, self.budget_term, )
127
128
129class Transaction(models.Model):
130    name = models.CharField(max_length=40)
131    desc = models.TextField(blank=True)
132
133    def __unicode__(self,):
134        return self.name
135
136def make_transfer(name, amount,
137    layer, budget_term, from_area, to_area, desc, ):
138    tx = Transaction(
139        name=name,
140        desc=desc,
141    )
142    tx.save()
143
144    from_li = LineItem(
145        label='Send: %s' % (name, ),
146        amount=-amount,
147        budget_area=from_area,
148        budget_term=budget_term,
149        layer=layer,
150        tx=tx,
151    )
152    from_li.save()
153
154    to_li = LineItem(
155        label='Receive: %s' % (name, ),
156        amount=amount,
157        budget_area=to_area,
158        budget_term=budget_term,
159        layer=layer,
160        tx=tx,
161    )
162    to_li.save()
163
164    return tx
165
166
167class LineItem(models.Model):
168    tx = models.ForeignKey(Transaction)
169    amount = models.DecimalField(max_digits=7, decimal_places=2, help_text='Do not include "$"')
170    label = models.CharField(max_length=60)
171    budget_area = models.ForeignKey(BudgetArea)
172    budget_term = models.ForeignKey(BudgetTerm)
173    layer = models.IntegerField() # this might actually be a Transaction property...
174
175    def layer_string(self,):
176        layer = self.layer
177        return layer_name(get_layer_by_num(layer))
178
179    def __unicode__(self, ):
180        return "%s: %s: $%s (%s) in %s during %s" % (
181            self.tx, self.label, self.amount, self.layer,
182            self.budget_area, self.budget_term, )
183
184
185LAYER_BUDGET      = 10
186LAYER_ALLOCATION  = 20
187LAYER_EXPENDITURE = 30
188LAYER_CLOSEOUT    = 40
189layers=(
190    (LAYER_BUDGET,      'budget'),
191    (LAYER_ALLOCATION,  'allocation'),
192    (LAYER_EXPENDITURE, 'expenditure'),
193    (LAYER_CLOSEOUT,    'closeout'),
194)
195def get_layer_by_name(name):
196    for layer in layers:
197        if name == layer[1]:
198            return layer
199    raise KeyError, "layer %s not found" % (name, )
200def get_layer_by_num(num):
201    for layer in layers:
202        if num == layer[0]:
203            return layer
204    raise KeyError, "layer %d not found" % (num, )
205def layer_name(layer): return layer[1]
206def layer_num(layer):  return layer[0]
207
208
209def coerce_full_email(email):
210    if '@' in email: return email
211    else: return email + '@' + settings.DEFAULT_DOMAIN
Note: See TracBrowser for help on using the repository browser.