]> asedeno.scripts.mit.edu Git - bluechips.git/blobdiff - bluechips/controllers/spend.py
if we GET to an update action, redirect to the edit action
[bluechips.git] / bluechips / controllers / spend.py
index 1a1c34612091b888df800ebeacfba199fc631da3..8d2dc4a526e182f8f80430babb5be0452b53da78 100644 (file)
@@ -4,107 +4,133 @@ Handle expenditures
 
 import logging
 
+from decimal import Decimal, InvalidOperation
+
 from bluechips.lib.base import *
-from bluechips.widgets import spend
 
-from pylons import request
+from pylons import request, app_globals as g
 from pylons.decorators.rest import dispatch_on
+from pylons.decorators import validate
+from pylons.controllers.util import abort
 
-from decimal import Decimal, InvalidOperation
+from formencode import validators, Schema
+from formencode.foreach import ForEach
+from formencode.variabledecode import NestedVariables
+from formencode.schema import SimpleFormValidator
+
+from mailer import Message
 
 log = logging.getLogger(__name__)
 
+
+class ShareSchema(Schema):
+    "Validate individual user shares."
+    allow_extra_fields = False
+    user_id = validators.Int(not_empty=True)
+    amount = validators.Number(not_empty=True)
+
+
+def validate_state(value_dict, state, validator):
+    if all(s['amount'] == 0 for s in value_dict['shares']):
+        return {'shares-0.amount': 'Need at least one non-zero share'}
+ValidateNotAllZero = SimpleFormValidator(validate_state)
+
+
+class ExpenditureSchema(Schema):
+    "Validate an expenditure."
+    allow_extra_fields = False
+    pre_validators = [NestedVariables()]
+    spender_id = validators.Int(not_empty=True)
+    amount = model.types.CurrencyValidator(not_empty=True)
+    description = validators.UnicodeString()
+    date = validators.DateConverter()
+    shares = ForEach(ShareSchema)
+    chained_validators = [ValidateNotAllZero]
+    
+
 class SpendController(BaseController):
     def index(self):
-        c.title = 'Add a New Expenditure'
-        
-        c.expenditure = dict()
-        c.expenditure['spender'] = request.environ['user']
-        
-        return render('/spend/index.mako')
+        return self.edit()
     
-    def edit(self, id):
-        c.title = 'Edit an Expenditure'
-        
-        c.expenditure = meta.Session.query(model.Expenditure).get(id)
-        
+    def edit(self, id=None):
+        c.users = meta.Session.query(model.User.id, model.User)
+        if id is None:
+            c.title = 'Add a New Expenditure'
+            c.expenditure = model.Expenditure()
+            c.expenditure.spender_id = request.environ['user'].id
+
+            num_residents = meta.Session.query(model.User).\
+                    filter_by(resident=True).count()
+            # Pre-populate split percentages for an even split.
+            c.values = {}
+            for ii, user_row in enumerate(c.users):
+                user_id, user = user_row
+                if user.resident:
+                    val = Decimal(100) / Decimal(num_residents)
+                else:
+                    val = 0
+                c.values['shares-%d.amount' % ii] = val
+        else:
+            c.title = 'Edit an Expenditure'
+            c.expenditure = meta.Session.query(model.Expenditure).get(id)
+            if c.expenditure is None:
+                abort(404)
+            c.values = {}
+            for ii, user_row in enumerate(c.users):
+                user_id, user = user_row
+                try:
+                    share = [s.share for s in c.expenditure.splits
+                             if s.user == user][0]
+                    if c.expenditure.amount == 0:
+                        percent = 0
+                    else:
+                        percent = (Decimal(100) * Decimal(int(share)) /
+                                   Decimal(int(c.expenditure.amount))).\
+                                quantize(Decimal("0.001"))
+                except IndexError:
+                    percent = 0
+                c.values['shares-%d.amount' % ii] = percent
+
         return render('/spend/index.mako')
-    
+
+    @redirect_on_get('edit')
+    @validate(schema=ExpenditureSchema(), form='edit', variable_decode=True)
     def update(self, id=None):
-        # Validate the submission
-        if not valid(self, spend.new_spend_form):
-            if id is None:
-                return self.index()
-            else:
-                return self.edit(id)
-        
         # Either create a new object, or, if we're editing, get the
         # old one
         if id is None:
             e = model.Expenditure()
             meta.Session.add(e)
+            op = 'created'
         else:
             e = meta.Session.query(model.Expenditure).get(id)
+            if e is None:
+                abort(404)
+            op = 'updated'
         
         # Set the fields that were submitted
+        shares = self.form_result.pop('shares')
         update_sar(e, self.form_result)
-        
-        if id is None:
-            e.even_split()
-        else:
-            e.update_split()
-        
-        meta.Session.commit()
-        
-        h.flash('Expenditure recorded.')
-        h.flash("""Want to do something unusual?
-
-<ul id="expenditure_options">
-  <li>%s</li>
-  <li>%s</li>
-</ul>""" % (h.link_to('Change the split', h.url_for(controller='spend',
-                                                   action='split',
-                                                   id=e.id)),
-           h.link_to('Spin off a subitem', h.url_for(controller='spend',
-                                                     action='subitem',
-                                                     id=e.id))))
-        
-        return h.redirect_to('/')
-    
-    @dispatch_on(POST='_post_split',
-                 GET='_get_split')
-    def split(self, id):
-        abort(500)
-    
-    def _get_split(self, id):
-        c.title = 'Change Expenditure Split'
-        
-        c.expenditure = meta.Session.query(model.Expenditure).get(id)
-        c.users = meta.Session.query(model.User)
-        
-        return render('/spend/split.mako')
-    
-    def _post_split(self, id):
-        c.values = request.params
-        c.errors = dict()
-        
-        split_dict = dict()
-        
-        for username, percent in c.values.iteritems():
-            try:
-                user = meta.Session.query(model.User).\
-                    filter(model.User.username==username).one()
-                split_dict[user] = Decimal(percent)
-            except InvalidOperation:
-                c.errors[username] = 'Please enter a number'
-        if c.errors != dict():
-            return self._get_split(id)
-        
-        e = meta.Session.query(model.Expenditure).get(id)
+
+        users = dict(meta.Session.query(model.User.id, model.User).all())
+        split_dict = {}
+        for share_params in shares:
+            user = users[share_params['user_id']]
+            split_dict[user] = Decimal(str(share_params['amount']))
         e.split(split_dict)
         
         meta.Session.commit()
-        
-        h.flash('Expenditure redivided')
-        
+       
+        show = ("Expenditure of %s paid for by %s %s." %
+                (e.amount, e.spender, op))
+        h.flash(show)
+
+        # Send email notification to involved users if they have an email set.
+        involved_users = set(sp.user for sp in e.splits if sp.share != 0)
+        involved_users.add(e.spender)
+        body = render('/emails/expenditure.txt',
+                      extra_vars={'expenditure': e,
+                                  'op': op})
+        g.handle_notification(involved_users, show, body)
+
         return h.redirect_to('/')