X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=bluechips%2Fcontrollers%2Fspend.py;h=8d2dc4a526e182f8f80430babb5be0452b53da78;hb=01eb085c206c4ba88045fb88a2f1076622bd2585;hp=1a1c34612091b888df800ebeacfba199fc631da3;hpb=d24e23a6a01ed245e204c8c29fc10a5fbc6f8bc4;p=bluechips.git diff --git a/bluechips/controllers/spend.py b/bluechips/controllers/spend.py index 1a1c346..8d2dc4a 100644 --- a/bluechips/controllers/spend.py +++ b/bluechips/controllers/spend.py @@ -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? - -""" % (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('/')