source: remit/finance_core/models.py @ 5a72e32

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

Fix BudgetArea? child-adding bug

Prior to http://code.tabo.pe/django-treebeard/changeset/6dbd2b7c169c,
Treebeard requires "ordering = path?" to be set in a Meta clas
on your model. Unfortunately, I didn't realize that.

  • Property mode set to 100644
File size: 6.6 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    class Meta:
109        permissions = (
110            ('use_reporting', 'Can use basic reporting functionality',),
111        )
112        ordering = ['path']
113
114
115class BudgetTerm(models.Model):
116    name = models.CharField(max_length=20)
117    slug = models.SlugField(max_length=20)
118    start_date = models.DateField()
119    end_date = models.DateField()
120    submit_deadline = models.DateField()
121
122    def __unicode__(self,):
123        return "%s (%s to %s [due %s])" % (self.name, self.start_date, self.end_date, self.submit_deadline, )
124
125
126class BudgetAreaTerm(models.Model):
127    budget_area = models.ForeignKey(BudgetArea)
128    budget_term = models.ForeignKey(BudgetTerm)
129    comment = models.TextField(blank=True, )
130
131    def __unicode__(self,):
132        return "%s during %s" % (self.budget_area, self.budget_term, )
133
134
135class Transaction(models.Model):
136    name = models.CharField(max_length=40)
137    desc = models.TextField(blank=True)
138
139    def __unicode__(self,):
140        return self.name
141
142def make_transfer(name, amount,
143    layer, budget_term, from_area, to_area, desc, ):
144    tx = Transaction(
145        name=name,
146        desc=desc,
147    )
148    tx.save()
149
150    from_li = LineItem(
151        label='Send: %s' % (name, ),
152        amount=-amount,
153        budget_area=from_area,
154        budget_term=budget_term,
155        layer=layer,
156        tx=tx,
157    )
158    from_li.save()
159
160    to_li = LineItem(
161        label='Receive: %s' % (name, ),
162        amount=amount,
163        budget_area=to_area,
164        budget_term=budget_term,
165        layer=layer,
166        tx=tx,
167    )
168    to_li.save()
169
170    return tx
171
172
173class LineItem(models.Model):
174    tx = models.ForeignKey(Transaction)
175    amount = models.DecimalField(max_digits=7, decimal_places=2, help_text='Do not include "$"')
176    label = models.CharField(max_length=60)
177    budget_area = models.ForeignKey(BudgetArea)
178    budget_term = models.ForeignKey(BudgetTerm)
179    layer = models.IntegerField() # this might actually be a Transaction property...
180
181    def layer_string(self,):
182        layer = self.layer
183        return layer_name(get_layer_by_num(layer))
184
185    def __unicode__(self, ):
186        return "%s: %s: $%s (%s) in %s during %s" % (
187            self.tx, self.label, self.amount, self.layer,
188            self.budget_area, self.budget_term, )
189
190
191LAYER_BUDGET      = 10
192LAYER_ALLOCATION  = 20
193LAYER_EXPENDITURE = 30
194LAYER_CLOSEOUT    = 40
195layers=(
196    (LAYER_BUDGET,      'budget'),
197    (LAYER_ALLOCATION,  'allocation'),
198    (LAYER_EXPENDITURE, 'expenditure'),
199    (LAYER_CLOSEOUT,    'closeout'),
200)
201def get_layer_by_name(name):
202    for layer in layers:
203        if name == layer[1]:
204            return layer
205    raise KeyError, "layer %s not found" % (name, )
206def get_layer_by_num(num):
207    for layer in layers:
208        if num == layer[0]:
209            return layer
210    raise KeyError, "layer %d not found" % (num, )
211def layer_name(layer): return layer[1]
212def layer_num(layer):  return layer[0]
213
214
215def coerce_full_email(email):
216    if '@' in email: return email
217    else: return email + '@' + settings.DEFAULT_DOMAIN
Note: See TracBrowser for help on using the repository browser.