]> asedeno.scripts.mit.edu Git - bluechips.git/blob - bluechips/controllers/spend.py
cc6466fed32f95ef15365e9bc86ec53079773dc2
[bluechips.git] / bluechips / controllers / spend.py
1 """
2 Handle expenditures
3 """
4
5 import logging
6
7 from decimal import Decimal, InvalidOperation
8
9 from bluechips.lib.base import *
10
11 from pylons import request, app_globals as g
12 from pylons.decorators.rest import dispatch_on
13 from pylons.decorators import validate
14 from pylons.controllers.util import abort
15
16 from formencode import validators, Schema
17 from formencode.foreach import ForEach
18 from formencode.variabledecode import NestedVariables
19 from formencode.schema import SimpleFormValidator
20
21 from mailer import Message
22
23 log = logging.getLogger(__name__)
24
25
26 class ShareSchema(Schema):
27     "Validate individual user shares."
28     allow_extra_fields = False
29     user_id = validators.Int(not_empty=True)
30     amount = validators.Number(not_empty=True)
31
32
33 def validate_state(value_dict, state, validator):
34     if all(s['amount'] == 0 for s in value_dict['shares']):
35         return {'shares-0.amount': 'Need at least one non-zero share'}
36 ValidateNotAllZero = SimpleFormValidator(validate_state)
37
38
39 class ExpenditureSchema(Schema):
40     "Validate an expenditure."
41     allow_extra_fields = False
42     pre_validators = [NestedVariables()]
43     spender_id = validators.Int(not_empty=True)
44     amount = model.types.CurrencyValidator(not_empty=True)
45     description = validators.UnicodeString()
46     date = validators.DateConverter()
47     shares = ForEach(ShareSchema)
48     chained_validators = [ValidateNotAllZero]
49     
50
51 class SpendController(BaseController):
52     def index(self):
53         return self.edit()
54     
55     def edit(self, id=None):
56         c.users = meta.Session.query(model.User.id, model.User)
57         if id is None:
58             c.title = 'Add a New Expenditure'
59             c.expenditure = model.Expenditure()
60             c.expenditure.spender_id = request.environ['user'].id
61
62             num_residents = meta.Session.query(model.User).\
63                     filter_by(resident=True).count()
64             # Pre-populate split percentages for an even split.
65             c.values = {}
66             for ii, user_row in enumerate(c.users):
67                 user_id, user = user_row
68                 if user.resident:
69                     val = Decimal(100) / Decimal(num_residents)
70                 else:
71                     val = 0
72                 c.values['shares-%d.amount' % ii] = val
73         else:
74             c.title = 'Edit an Expenditure'
75             c.expenditure = meta.Session.query(model.Expenditure).get(id)
76             if c.expenditure is None:
77                 abort(404)
78             c.values = {}
79             for ii, user_row in enumerate(c.users):
80                 user_id, user = user_row
81                 try:
82                     share = [s.share for s in c.expenditure.splits
83                              if s.user == user][0]
84                     if c.expenditure.amount == 0:
85                         percent = 0
86                     else:
87                         percent = (Decimal(100) * Decimal(int(share)) /
88                                    Decimal(int(c.expenditure.amount))).\
89                                 quantize(Decimal("0.001"))
90                 except IndexError:
91                     percent = 0
92                 c.values['shares-%d.amount' % ii] = percent
93
94         return render('/spend/index.mako')
95
96     @validate(schema=ExpenditureSchema(), form='edit', variable_decode=True)
97     def update(self, id=None):
98         # Either create a new object, or, if we're editing, get the
99         # old one
100         if id is None:
101             e = model.Expenditure()
102             meta.Session.add(e)
103             op = 'created'
104         else:
105             e = meta.Session.query(model.Expenditure).get(id)
106             if e is None:
107                 abort(404)
108             op = 'updated'
109         
110         # Set the fields that were submitted
111         shares = self.form_result.pop('shares')
112         update_sar(e, self.form_result)
113
114         users = dict(meta.Session.query(model.User.id, model.User).all())
115         split_dict = {}
116         for share_params in shares:
117             user = users[share_params['user_id']]
118             split_dict[user] = Decimal(str(share_params['amount']))
119         e.split(split_dict)
120         
121         meta.Session.commit()
122        
123         show = ("Expenditure of %s paid for by %s %s." %
124                 (e.amount, e.spender, op))
125         h.flash(show)
126
127         # Send email notification to involved users if they have an email set.
128         involved_users = set(sp.user for sp in e.splits if sp.share != 0)
129         involved_users.add(e.spender)
130         body = render('/emails/expenditure.txt',
131                       extra_vars={'expenditure': e,
132                                   'op': op})
133         g.handle_notification(involved_users, show, body)
134
135         return h.redirect_to('/')