]> asedeno.scripts.mit.edu Git - bluechips.git/blob - bluechips/controllers/spend.py
8d2dc4a526e182f8f80430babb5be0452b53da78
[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     @redirect_on_get('edit')
97     @validate(schema=ExpenditureSchema(), form='edit', variable_decode=True)
98     def update(self, id=None):
99         # Either create a new object, or, if we're editing, get the
100         # old one
101         if id is None:
102             e = model.Expenditure()
103             meta.Session.add(e)
104             op = 'created'
105         else:
106             e = meta.Session.query(model.Expenditure).get(id)
107             if e is None:
108                 abort(404)
109             op = 'updated'
110         
111         # Set the fields that were submitted
112         shares = self.form_result.pop('shares')
113         update_sar(e, self.form_result)
114
115         users = dict(meta.Session.query(model.User.id, model.User).all())
116         split_dict = {}
117         for share_params in shares:
118             user = users[share_params['user_id']]
119             split_dict[user] = Decimal(str(share_params['amount']))
120         e.split(split_dict)
121         
122         meta.Session.commit()
123        
124         show = ("Expenditure of %s paid for by %s %s." %
125                 (e.amount, e.spender, op))
126         h.flash(show)
127
128         # Send email notification to involved users if they have an email set.
129         involved_users = set(sp.user for sp in e.splits if sp.share != 0)
130         involved_users.add(e.spender)
131         body = render('/emails/expenditure.txt',
132                       extra_vars={'expenditure': e,
133                                   'op': op})
134         g.handle_notification(involved_users, show, body)
135
136         return h.redirect_to('/')