source: remit/finance_core/models.py @ ee82431

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

Gracefully handle no account having an owner

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