source: remit/finance_core/models.py @ 02030a2

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

Allow getting nodes by path relative to a base

  • Property mode set to 100644
File size: 6.7 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, base=None, ):
59        if base:
60            node = base
61        else:
62            try:
63                root = BudgetArea.objects.get(name=path[0], depth=1)
64            except IndexError, e:
65                raise KeyError(e)
66            node = root
67            path = path[1:]
68        for name in path:
69            try:
70                node = node.get_children().filter(name=name)[0]
71            except IndexError, e:
72                raise KeyError(e)
73        return node
74
75    @classmethod
76    def get_by_pathstr(cls, path, base=None):
77        path = path.split('.')
78        return cls.get_by_path(path, base=base, )
79
80    def pathstr(self, skip=0):
81        if self.depth-1 > skip:
82            parent = self.get_parent()
83            prefix = parent.pathstr(skip=skip) + '.'
84        else:
85            prefix = ''
86        return prefix + self.name
87
88    def dump_to_html(self):
89        struct = self.dump_bulk()
90        return self.struct_to_html(struct, depth=0)
91
92    def struct_to_html(self, struct, depth=0):
93        def format_data(data):
94            return "<em>"+data['name']+"</em> "+unicode(data)
95        prefix = "\t"*depth
96        html = prefix+"<ul>\n"
97        html = html + "\n".join(
98            [("%(prefix)s\t<li>%(data)s\n%(children)s\t%(prefix)s</li>\n" % {
99                'prefix':prefix,
100                'data':format_data(elem['data']),
101                'children':('children' in elem and self.struct_to_html(elem['children'], depth+1) or '')
102            }) for elem in struct])
103        html = html + prefix + "</ul>\n"
104        return html
105
106    def indented_name(self, strip_levels=0):
107        return self.indent_str*(self.depth-strip_levels) + unicode(self)
108
109    def __unicode__(self,):
110        return u"%s [%s] (%s)" % (self.name, self.contact_address(), self.always, )
111
112    class Meta:
113        permissions = (
114            ('use_reporting', 'Can use basic reporting functionality',),
115        )
116        ordering = ['path']
117
118
119class BudgetTerm(models.Model):
120    name = models.CharField(max_length=20)
121    slug = models.SlugField(max_length=20)
122    start_date = models.DateField()
123    end_date = models.DateField()
124    submit_deadline = models.DateField()
125
126    def __unicode__(self,):
127        return "%s (%s to %s [due %s])" % (self.name, self.start_date, self.end_date, self.submit_deadline, )
128
129
130class BudgetAreaTerm(models.Model):
131    budget_area = models.ForeignKey(BudgetArea)
132    budget_term = models.ForeignKey(BudgetTerm)
133    comment = models.TextField(blank=True, )
134
135    def __unicode__(self,):
136        return "%s during %s" % (self.budget_area, self.budget_term, )
137
138
139class Transaction(models.Model):
140    name = models.CharField(max_length=40)
141    desc = models.TextField(blank=True)
142
143    def __unicode__(self,):
144        return self.name
145
146def make_transfer(name, amount,
147    layer, budget_term, from_area, to_area, desc, ):
148    tx = Transaction(
149        name=name,
150        desc=desc,
151    )
152    tx.save()
153
154    from_li = LineItem(
155        label='Send: %s' % (name, ),
156        amount=-amount,
157        budget_area=from_area,
158        budget_term=budget_term,
159        layer=layer,
160        tx=tx,
161    )
162    from_li.save()
163
164    to_li = LineItem(
165        label='Receive: %s' % (name, ),
166        amount=amount,
167        budget_area=to_area,
168        budget_term=budget_term,
169        layer=layer,
170        tx=tx,
171    )
172    to_li.save()
173
174    return tx
175
176
177class LineItem(models.Model):
178    tx = models.ForeignKey(Transaction)
179    amount = models.DecimalField(max_digits=7, decimal_places=2, help_text='Do not include "$"')
180    label = models.CharField(max_length=60)
181    budget_area = models.ForeignKey(BudgetArea)
182    budget_term = models.ForeignKey(BudgetTerm)
183    layer = models.IntegerField() # this might actually be a Transaction property...
184
185    def layer_string(self,):
186        layer = self.layer
187        return layer_name(get_layer_by_num(layer))
188
189    def __unicode__(self, ):
190        return "%s: %s: $%s (%s) in %s during %s" % (
191            self.tx, self.label, self.amount, self.layer,
192            self.budget_area, self.budget_term, )
193
194
195LAYER_BUDGET      = 10
196LAYER_ALLOCATION  = 20
197LAYER_EXPENDITURE = 30
198LAYER_CLOSEOUT    = 40
199layers=(
200    (LAYER_BUDGET,      'budget'),
201    (LAYER_ALLOCATION,  'allocation'),
202    (LAYER_EXPENDITURE, 'expenditure'),
203    (LAYER_CLOSEOUT,    'closeout'),
204)
205def get_layer_by_name(name):
206    for layer in layers:
207        if name == layer[1]:
208            return layer
209    raise KeyError, "layer %s not found" % (name, )
210def get_layer_by_num(num):
211    for layer in layers:
212        if num == layer[0]:
213            return layer
214    raise KeyError, "layer %d not found" % (num, )
215def layer_name(layer): return layer[1]
216def layer_num(layer):  return layer[0]
217
218
219def coerce_full_email(email):
220    if '@' in email: return email
221    else: return email + '@' + settings.DEFAULT_DOMAIN
Note: See TracBrowser for help on using the repository browser.