def cancelPayment(request, pmtId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not Payment.objects.filter(pk=pmtId).count() > 0: return HttpResponseRedirect('/payments/') pmt = Payment.objects.get(pk=pmtId) if pmt.payer_id != userNode.id: return HttpResponseRedirect('/payments/') if pmt.status not in ('RQ', 'PE'): # requested, pending return HttpResponseRedirect('/payments/') if pmt.status == 'RQ': pmt.status = 'RF' # refused # *** todo: Send refusal email to requester. t = template_loader.get_template('emailPmtRequestRefused.txt') c = RequestContext(request, {'req': pmt}) sendEmail("Payment request refusal notification", t.render(c), pmt.recipient.getPrimaryEmail()) else: pmt.status = 'CA' # cancelled pmt.save() request.session['infos'] = ["Payment cancelled."] return HttpResponseRedirect('/payments/')
def new_ad(request): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) d = {} if request.method == 'POST': errors = [] if not request.POST.get('title'): errors.append('Your post must have a subject.') if not request.POST.get('text'): errors.append('Your post must have a body.') if not errors: ad = Advertisement(user=userNode, title=request.POST['title'], text=request.POST['text']) ad.save() request.session['infos'] = ['Your new post has been added.'] return HttpResponseRedirect('../') else: d['values'] = request.POST d['infos'] = errors d['infos'] = getSessionInfos(request) return render_to_response('new_ad.html', d, context_instance=RequestContext(request))
def accountAction(request, acctId, action): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/accounts/%s/' % acctId) if request.method == 'POST': if request.POST['submit'] == 'Cancel': return HttpResponseRedirect('/accounts/%s/' % acctId) if Account.objects.filter(pk=acctId).count() == 0: return HttpResponseRedirect('/accounts/') acct = Account.objects.get(pk=acctId) if acct.owner_id != userNode.id: return HttpResponseRedirect('/accounts/') if action == 'close': sharedData = acct.shared_data if abs(sharedData.balance) >= 0.005: request.session['infos'] = ["Account balance must be zero before it can be closed."] return HttpResponseRedirect('/accounts/%d/' % acct.id) # to be sure a transaction doesn't hit this account before it gets # deactivated, set limits to zero, save, and then check for zero balance again myIOULimit = acct.iou_limit partnerAcct = acct.get_partner_acct() partnerIOULimit = partnerAcct.iou_limit acct.iou_limit = 0.0 acct.save() partnerAcct.iou_limit = 0.0 partnerAcct.save() if abs(sharedData.balance) >= 0.005: request.session['infos'] = ["Account balance must be zero before it can be closed."] # restore limits on accts acct.iou_limit = myIOULimit acct.save() partnerAcct.iou_limit = partnerIOULimit partnerAcct.save() return HttpResponseRedirect('/accounts/%d/' % acct.id) # now it's impossible for a transaction to change the balance, and the balance is zero, so: sharedData.active = False sharedData.save() # notify user request.session['infos'] = ["Account %d (%s) closed." % (sharedData.id, acct.name)] # notify partner by email note = '' if request.method == 'POST': note = request.POST['note'] t = template_loader.get_template('emailAcctClosed.txt') c = RequestContext(request, {'acct':partnerAcct, 'note':note}) sendEmail("One of your accounts has been closed", t.render(c), acct.partner.getPrimaryEmail()) pass return HttpResponseRedirect('/accounts/')
def paymentDetail(request, pmtId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if Payment.objects.filter(pk=pmtId).count() == 0: return HttpResponseRedirect('/summary') pmt = Payment.objects.get(pk=pmtId) if pmt.status != 'OK': return HttpResponseRedirect('/summary') d = {} # get any messages from session d['infos'] = getSessionInfos(request) # get every payment link that touches this user inLinks = PaymentLink.objects.filter(path__payment=pmtId, payer_account__partner=userNode) outLinks = PaymentLink.objects.filter(path__payment=pmtId, payer_account__owner=userNode) if pmt.payer_id == userNode.id: assert outLinks, "User %d is payer for payment %d, but has no outgoing payment links." % (userNode.id, pmt.id) d['outPayment'] = True elif pmt.recipient_id == userNode.id: assert inLinks, "User is %d is recipient for payment %d, but has no incoming payment links." % (userNode.id, pmt.id) d['inPayment'] = True elif inLinks and outLinks: d['thruPayment'] = True else: # user should have no links at all assert not inLinks and not outLinks, "User %d is an intermediary for payment %d, but does not have both incoming and outgoing links." % (userNode.id, pmt.id) return HttpResponseRedirect('/summary') inAccts = [] for link in inLinks: acct = link.payer_account.get_partner_acct() if acct in inAccts: # fetch copy we already have and add to numbers acct = inAccts[inAccts.index(acct)] acct.inEntry += link.amount else: acct.inEntry = link.amount inAccts.append(acct) outAccts = [] for link in outLinks: acct = link.payer_account if acct in outAccts: # fetch copy we already have and add to numbers acct = outAccts[outAccts.index(acct)] acct.outEntry += link.amount else: acct.outEntry = link.amount outAccts.append(acct) d['pmt'] = pmt d['inAccts'] = inAccts d['outAccts'] = outAccts return render_to_response('paymentDetail.html', d, context_instance=RequestContext(request))
def main(request): "Market front page. View all ads in the last 30 days for now." userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) d = {} cutoff_date = datetime.now() - timedelta(days=30) ads = Advertisement.objects.filter(posted_date__gt=cutoff_date) ads = ads.order_by('-posted_date') d['ads'] = ads d['infos'] = getSessionInfos(request) return render_to_response('ad_list.html', d, context_instance=RequestContext(request))
def confirmClose(request, acctId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not Account.objects.filter(pk=acctId).count() > 0: return HttpResponseRedirect('/accounts/') d = {} d['acct'] = Account.objects.get(pk=acctId) if d['acct'].owner_id != userNode.id: return HttpResponseRedirect('/accounts/') return render_to_response('confirmClose.html', d, context_instance=RequestContext(request))
def pay(request, pmtId): """Transfer credit to somebody else's node""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=/payments/%s/confirm/' % pmtId) if not request.POST: return HttpResponseRedirect('/payments/') if not Payment.objects.filter(pk=pmtId).count() > 0: return HttpResponseRedirect('/payments/') pmt = Payment.objects.get(pk=pmtId) if pmt.payer_id != userNode.id: return HttpResponseRedirect('/payments/') if pmt.status != 'PE': # pending return HttpResponseRedirect('/payments/') # make payment maxNodes = 1e20 # infinite - search all nodes collisions = 3 while True: # retry until break or return try: performPayment(pmt, maxNodes=maxNodes) message = "Successfully paid %s %.2f to %s." % ( pmt.currency.short_name, pmt.amount, escape( pmt.recipient_email)) request.session['infos'] = [message] sendPaymentEmails(pmt, request) break except PaymentError, e: print e if e.retry and collisions > 0: # retry collisions -= 1 else: # quit if collisions == 0: request.session['infos'] = [ 'Credit found, but payment transaction still not able to be committed after four attempts. May be due to high system activity.' ] else: request.session['infos'] = [e.message] if e.retry: request.session['infos'].append( '<a href="/payments/%d/edit/">Click here to retry.</a>' % pmt.id) elif e.amountLacking <> pmt.amount: request.session['infos'].append( '<a href="/payments/%d/edit/">Click here to retry with a smaller amount.</a>' % pmt.id) break
def rejectionNote(request, offerId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if Offer.objects.filter(pk=offerId).count() == 0: request.session['infos'] = ["Offer has been withdrawn by the sender."] return HttpResponseRedirect('/summary/') offer = Offer.objects.get(pk=offerId) if offer.recipient_email not in getEmails(userNode): return HttpResponseRedirect('/summary/') d = {'offer': offer} return render_to_response('rejectionNote.html', d, context_instance=RequestContext(request))
def accountList(request): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) d = {} d['infos'] = getSessionInfos(request) d['accountCurrencies'] = getAccountsSummary(userNode) # outstanding offers of credit received/made d['receivedOffers'] = Offer.objects.filter(recipient_email__in=getEmails(userNode)).order_by('-id') d['sentOffers'] = userNode.sent_offer_set.order_by('-id') d['rateProposals'] = getRateProposalAccts(userNode) return render_to_response('accountList.html', d, context_instance=RequestContext(request))
def paymentList(request): d = {} userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) d['infos'] = getSessionInfos(request) d['userNode'] = userNode d['payments'] = Payment.objects.filter(Q(payer=userNode) | Q(recipient=userNode), status='OK').order_by('-date') prepPaymentsForDisplay(d['payments'], userNode) d['receivedPaymentRequests'] = Payment.objects.filter(payer=userNode, status='RQ' ).order_by('-date') d['sentPaymentRequests'] = Payment.objects.filter(recipient=userNode, status='RQ' ).order_by('-date') return render_to_response('paymentList.html', d, context_instance=RequestContext(request))
def interestRateAction(request, acctId, action): "Accept or reject a rate-change proposal on an account." userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if Account.objects.filter(owner=userNode, pk=acctId).count() == 0: return HttpResponseRedirect('/accounts/') acct = Account.objects.get(pk=acctId) shared_data = acct.shared_data if shared_data.proposed_rate <> None and shared_data.node_to_confirm_id == userNode.id: if action == 'accept': accept_proposed_rate(request, shared_data) elif action == 'reject': reject_proposed_rate(request, shared_data) return HttpResponseRedirect('/accounts/%d/' % acct.id)
def view_ad(request, ad_id): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) try: ad = Advertisement.objects.get(pk=ad_id) except Advertisement.DoesNotExist: return HttpResponseRedirect('../') d = {} if request.method == 'POST': # *** todo: actually send an email here. request.session['infos'] = ['Your message has been sent to the seller.'] return HttpResponseRedirect('.') d['ad'] = ad d['infos'] = getSessionInfos(request) return render_to_response('view_ad.html', d, context_instance=RequestContext(request))
def accountList(request): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) d = {} d['infos'] = getSessionInfos(request) d['accountCurrencies'] = getAccountsSummary(userNode) # outstanding offers of credit received/made d['receivedOffers'] = Offer.objects.filter( recipient_email__in=getEmails(userNode)).order_by('-id') d['sentOffers'] = userNode.sent_offer_set.order_by('-id') d['rateProposals'] = getRateProposalAccts(userNode) return render_to_response('accountList.html', d, context_instance=RequestContext(request))
def pay(request, pmtId): """Transfer credit to somebody else's node""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=/payments/%s/confirm/' % pmtId) if not request.POST: return HttpResponseRedirect('/payments/') if not Payment.objects.filter(pk=pmtId).count() > 0: return HttpResponseRedirect('/payments/') pmt = Payment.objects.get(pk=pmtId) if pmt.payer_id != userNode.id: return HttpResponseRedirect('/payments/') if pmt.status != 'PE': # pending return HttpResponseRedirect('/payments/') # make payment maxNodes = 1e20 # infinite - search all nodes collisions = 3 while True: # retry until break or return try: performPayment(pmt, maxNodes=maxNodes) message = "Successfully paid %s %.2f to %s." % (pmt.currency.short_name, pmt.amount, escape(pmt.recipient_email)) request.session['infos'] = [message] sendPaymentEmails(pmt, request) break except PaymentError, e: print e if e.retry and collisions > 0: # retry collisions -= 1 else: # quit if collisions == 0: request.session['infos'] = ['Credit found, but payment transaction still not able to be committed after four attempts. May be due to high system activity.'] else: request.session['infos'] = [e.message] if e.retry: request.session['infos'].append('<a href="/payments/%d/edit/">Click here to retry.</a>' % pmt.id) elif e.amountLacking <> pmt.amount: request.session['infos'].append('<a href="/payments/%d/edit/">Click here to retry with a smaller amount.</a>' % pmt.id) break
def paymentList(request): d = {} userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) d['infos'] = getSessionInfos(request) d['userNode'] = userNode d['payments'] = Payment.objects.filter(Q(payer=userNode) | Q(recipient=userNode), status='OK').order_by('-date') prepPaymentsForDisplay(d['payments'], userNode) d['receivedPaymentRequests'] = Payment.objects.filter( payer=userNode, status='RQ').order_by('-date') d['sentPaymentRequests'] = Payment.objects.filter( recipient=userNode, status='RQ').order_by('-date') return render_to_response('paymentList.html', d, context_instance=RequestContext(request))
def confirmPayment(request, pmtId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not Payment.objects.filter(pk=pmtId).count() > 0: return HttpResponseRedirect('/payments/') pmt = Payment.objects.get(pk=pmtId) if pmt.payer_id != userNode.id: return HttpResponseRedirect('/payments/') if pmt.status != 'PE': # pending return HttpResponseRedirect('/payments/') d = {} d['pmt'] = Payment.objects.get(pk=pmtId) if d['pmt'].payer_id != userNode.id: return HttpResponseRedirect('/payments/') return render_to_response('confirmPayment.html', d, context_instance=RequestContext(request))
def view_ad(request, ad_id): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) try: ad = Advertisement.objects.get(pk=ad_id) except Advertisement.DoesNotExist: return HttpResponseRedirect('../') d = {} if request.method == 'POST': # *** todo: actually send an email here. request.session['infos'] = [ 'Your message has been sent to the seller.' ] return HttpResponseRedirect('.') d['ad'] = ad d['infos'] = getSessionInfos(request) return render_to_response('view_ad.html', d, context_instance=RequestContext(request))
def paymentForm(request, pmtId=None, otherUserId=None, is_request=False): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) errors = [] d = {} if request.method == 'POST': # get post data and attempt payment email = request.POST['email'] d['email'] = email # for maintaining form value in case of error if EmailAddr.objects.filter(email=email, confirmed=True).count() == 0: errors.append("Email '%s' does not belong to any Ripple user," "or has not yet been confirmed." % escape(email)) else: emailAddr = EmailAddr.objects.get(email=email) otherUser = emailAddr.node if otherUser == userNode: action = is_request and 'request payment from' or 'pay' errors.append("You can't %s yourself." % action) amountStr = request.POST['amount'] d['amount'] = amountStr try: amount = float(amountStr) if amount <= 0.0: raise Exception except: errors.append("Amount must be a positive number. '%s' is not valid." % escape(amountStr)) currencyName = request.POST['currency'] # this should be OK, since it's from a generated list currency = CurrencyUnit.objects.get(short_name=currencyName) d['selectedUnitId'] = currency.id d['description'] = request.POST['description'] if not errors: # check for enough credit available = availableCredit(userNode, currency, incoming=is_request) if available < amount: action = is_request and 'request' or 'make' errors.append("You do not have enough available credit to %s payment. " "Available: %s %.2f." % (action, currency.short_name, available)) if availableCredit(otherUser, currency, incoming=not is_request) < amount: if is_request: pass # Other user might get more credit to fulfill request else: errors.append("Your intended recipient has not offered enough credit " "to receive your payment.") pmt = None if request.POST['pmtId'] != 'None': pmt = Payment.objects.get(pk=request.POST['pmtId']) # Check if user can edit if (not is_request and pmt.payer_id != userNode.id) or \ (is_request and pmt.recipient_id != userNode.id): return HttpResponseRedirect('/payments/') if pmt.status == 'OK': # can't edit completed payment d = {'message':'Payment has already been completed.', 'link':'/payments/'} return render_to_response('error.html', d, context_instance=RequestContext(request)) d['pmtId'] = pmt.id # in case of errors, re-post pmtId if not errors: payer = is_request and otherUser or userNode recipient = is_request and userNode or otherUser if pmt is None: pmt = Payment() # Create new Payment. pmt.payer = payer pmt.payer_email = is_request and email or payer.getPrimaryEmail() pmt.recipient = recipient pmt.recipient_email = is_request and recipient.getPrimaryEmail() or email pmt.amount = amount pmt.currency = currency pmt.date = datetime.now() pmt.status = is_request and 'RQ' or 'PE' pmt.description = d['description'] pmt.save() if is_request: # Send payment request email t = template_loader.get_template('emailPmtRequestReceived.txt') c = RequestContext(request, {'req': pmt}) sendEmail("Payment request notification", t.render(c), pmt.payer_email) request.session['infos'] = ["Your payment request has been recorded " "and a notification email sent to the payer."] return HttpResponseRedirect('/payments/') else: # Go to payment confirmation page return HttpResponseRedirect('/payments/%s/confirm/' % pmt.id) # present make payment form d['infos'] = errors if otherUserId: # paying a specific user - URL = payUser/id/ if Node.objects.filter(pk=otherUserId).count() > 0 and userNode.id != otherUserId: d['email'] = Node.objects.get(pk=otherUserId).getPrimaryEmail() if pmtId: # editing a pending payment from DB - URL = paymentForm/id/ if Payment.objects.filter(pk=pmtId).count() > 0: pmt = Payment.objects.get(pk=pmtId) if pmt.status == 'OK': # can't edit completed payment d = {'message':'Payment has already been completed.', 'link':'/payments/'} return render_to_response('error.html', d, context_instance=RequestContext(request)) d['email'] = is_request and pmt.payer_email or pmt.recipient_email d['amount'] = pmt.amount d['selectedUnitId'] = pmt.currency_id d['description'] = pmt.description d['pmtId'] = pmtId d['paymentUnits'] = makeUnitsList(userNode) # set selected units to account units, if paying account partner, or to default units if not d.has_key('selectedUnitId'): if otherUserId and userNode.account_set.filter(partner__pk=otherUserId).count() > 0: acct = userNode.account_set.filter(partner__pk=otherUserId)[0] d['selectedUnitId'] = acct.shared_data.currency_id else: d['selectedUnitId'] = userNode.display_units_id d['is_request'] = is_request return render_to_response('paymentForm.html', d, context_instance=RequestContext(request))
def offer(request): """offer credit to multiple nodes""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not request.POST: return HttpResponseRedirect('/summary/') if request.POST['phase'] == 'make_offers': # check if offers are finalized, or if we're just listing emails and the form # extract info from POST dictionary rows = [] for i in range(0, int(request.POST['count'])): row = Struct() row.email = request.POST['email_%d' % i] # already parsed as email addresses row.amount = request.POST['amount_%d' % i] row.currency = request.POST['currency_%d' % i] row.interest = request.POST['interest_%d' % i] rows.append(row) # find errors in entries error = False for row in rows: # ensure no double-offers or double-accounts emailList, errors = validateOfferEmails([row.email], userNode) if not emailList: # if row.email is in list, then offer is valid error = True row.error = errors[0] # only show first error continue # only one error per row # validate amounts as numbers try: row.amount = float(row.amount) if row.amount <= 0.0: raise ValueError except ValueError: error = True row.error = "Credit limit must be a number greater than zero." else: if row.amount >= 10**12: error = True row.error = "Amount is too large to be stored in the database. Please enter another amount." elif row.amount <= 10**-12: error = True row.error = "Amount is too small to be stored in the database. Please enter another amount." if error: continue # only one error per row try: row.interest = float(row.interest) except ValueError: error = True row.error = "Interest rate must be a number." note = request.POST['note'] if note == '': note = None if error: # show form again with errors and values still there (in row objects) d = {'rows':rows, 'count':len(rows)} d['infos'] = ["One of more of your entries had errors. Please make corrections below."] else: # create and save offers, and send out emails infos = [] infos.append('The system has recorded your offers of credit and is sending out invitation emails. <a href="/accounts/#offers">Click here to see your offers.</a>') #infos.append("If the invitation does not arrive, it may be in the destination's spam folder...") for row in rows: currency = CurrencyUnit.objects.get(short_name=row.currency) offer = Offer(initiator=userNode, recipient_email=row.email, amount=row.amount, currency=currency, interest_rate=row.interest) offer.save() # send out email t = template_loader.get_template('emailInvitation.txt') c = RequestContext(request, {'row':row, 'userNode':userNode, 'currency':currency, 'note':note}) sender = '"%s" <%s>' % (userNode.name, userNode.getPrimaryEmail()) if not sendEmail('Invitation to Ripple', t.render(c), row.email, sender=sender, attempts=2, includeServiceName=False): infos.append("An email could not be sent to %s, but your offer has been stored. Please contact %s manually." % (escape(row.email), escape(row.email))) request.session['infos'] = infos return HttpResponseRedirect('/accounts/') else: # else, list emails + form emails = parseEmails(request.POST['emailText']) emails, infos = validateOfferEmails(emails, userNode) if emails == [] and infos == []: infos.append("You didn't enter any valid email addresses.") rows = [] for email in emails: row = Struct() row.email = email row.currency = None row.interest = 0 rows.append(row) d = {'infos':infos, 'rows':rows, 'count':len(rows)} d['currencies'] = CurrencyUnit.objects.order_by('long_name') return render_to_response('offer.html', d, context_instance=RequestContext(request))
def offerAction(request, offerId, action): """Accept or reject somebodys offer of credit""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) message = '' if Offer.objects.filter(pk=offerId).count() == 1: offer = Offer.objects.get(pk=offerId) else: # offer has been deleted since action link was displayed to user if action == 'accept' or action == 'reject': message = "Offer has been withdrawn and cannot be accepted or rejected." else: message = "Offer has already been acted on and cannot be withdrawn." request.session['infos'] = [message] return HttpResponseRedirect('/summary/') # check the offer is for the logged-in user if action is accept or reject if (action == 'accept' or action == 'reject') and \ offer.recipient_email not in getEmails(userNode): return HttpResponseRedirect('/summary/') if action == 'accept': initiator = offer.initiator # check for existing account between nodes # *** temporary until multiple accounts permitted if Account.objects.filter(owner=userNode, partner=initiator, shared_data__active=True).count() > 0: request.session['infos'] = ["Account already exists with %s. Multiple accounts with the same neighbour are not permitted (yet)." % escape(initiator.name)] offer.delete() return HttpResponseRedirect('/summary/') currencyId = offer.currency_id shared = SharedAccountData(currency_id=currencyId, balance=0.0, active=True, interest_rate=offer.interest_rate/100.0) shared.save() acct1 = Account(owner=initiator, partner=userNode, shared_data=shared, balance_multiplier=1, iou_limit=0.0, partner_limit=offer.amount, name=userNode.name) acct1.save() acct2 = Account(owner=userNode, partner=initiator, shared_data=shared, balance_multiplier=-1, iou_limit=offer.amount, partner_limit=0.0, name=initiator.name) acct2.save() offer.delete() # shuffle node locations *** slow - consider doing as a regular script instead circle = routing.Circle() circle.shuffle(10 * Node.objects.count()) # send email informing initiator that offer was accepted t = template_loader.get_template('emailInvitationAccepted.txt') c = RequestContext(request, {'offer':offer, 'acct':acct1}) sendEmail("Invitation accepted", t.render(c), initiator.getPrimaryEmail()) request.session['infos'] = ["This is your new connection account with %s. Please take the time to give your account a meaningful name and assign your new neighbour a credit limit." % escape(initiator.getPrimaryEmail())] return HttpResponseRedirect('/accounts/%d/' % acct2.id) elif action == 'reject': offer.delete() note = '' if request.method == 'POST': note = request.POST['note'] # send email informing initiator that offer was rejected t = template_loader.get_template('emailInvitationRejected.txt') c = RequestContext(request, {'offer':offer, 'note':note}) sendEmail("Your invitation was not accepted", t.render(c), offer.initiator.getPrimaryEmail()) message = "Offer from %s rejected." % escape(offer.initiator.getPrimaryEmail()) elif action == 'withdraw': if userNode != offer.initiator: return HttpResponseRedirect('/accounts/') offer.delete() message = "Offer to %s withdrawn." % offer.recipient_email if message: request.session['infos'] = [message] return HttpResponseRedirect('/accounts/')
def accountDetail(request, acctId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if Account.objects.filter(pk=acctId).count() > 0: acct = Account.objects.get(pk=acctId) else: # can't access accounts that aren't in database and active return HttpResponseRedirect('/accounts/') if acct.owner_id != userNode.id: # can't access accounts that aren't yours! return HttpResponseRedirect('/accounts/') d = {} d['infos'] = getSessionInfos(request) if request.method == 'POST': # modify account or set error if request.POST['name'] == '': d['infos'].append("Error: Account name can't be left blank.") elif acct.name != request.POST['name']: acct.name = request.POST['name'] d['infos'].append("Account name changed.") try: interest_rate = float(request.POST['interest']) / 100.0 # may raise ValueError shared_data = acct.shared_data if interest_rate <> shared_data.interest_rate: if shared_data.proposed_rate and interest_rate == shared_data.proposed_rate and userNode.id == shared_data.node_to_confirm_id: # take manually setting to otherly-proposed rate to be acceptance accept_proposed_rate(request, shared_data) d['infos'].append("Interest rate set to %s%%." % (interest_rate * 100.0)) else: info = propose_rate(request, shared_data, interest_rate, node_to_confirm=acct.partner) d['infos'].append(info) except ValueError: d['infos'].append("Interest rate must be a number.") try: # modify partner's max credit limit partner_limit = float(request.POST['partner_limit']) if partner_limit < 0.0: d['infos'].append("Error: Partner's credit limit must be a positive number or zero. '%s' is invalid." % request.POST['partner_limit']) raise ValueError elif partner_limit >= 1e12: d['infos'].append("Error: Partner limit amount is too large to be stored in the database. Please enter another amount.") raise ValueError elif partner_limit <= 1e-12 and partner_limit != 0.0: d['infos'].append("Error: Partner limit amount is too small to be stored in the database. Please enter another amount.") raise ValueError if acct.partner_limit != partner_limit: acct.partner_limit = partner_limit d['infos'].append("Partner has been offered a credit limit of up to %.2f." % acct.partner_limit) # modify partner's actual iou limit if necessary partnerAcct = acct.get_partner_acct() oldPartnerIOULimit = partnerAcct.iou_limit if partnerAcct.my_limit != None: partnerAcct.iou_limit = min(partnerAcct.my_limit, acct.partner_limit) else: partnerAcct.iou_limit = acct.partner_limit if partnerAcct.iou_limit != oldPartnerIOULimit: partnerAcct.save() if acct.partner_limit == partnerAcct.iou_limit: d['infos'].pop() # clear mesage about partner being offered credit d['infos'].append("Partner's credit limit changed to %.2f." % partnerAcct.iou_limit) except ValueError: d['infos'].append("Partner's credit limit must be a number.") if request.POST['my_limit'] != '': try: # modify my max credit limit my_limit = float(request.POST['my_limit']) if my_limit < 0.0: d['infos'].append("Error: My credit limit must be a positive number or zero. '%s' is invalid." % request.POST['my_limit']) raise ValueError elif my_limit >= 1e12: d['infos'].append("Error: My credit limit amount is too large to be stored in the database. Please enter another amount.") raise ValueError elif my_limit <= 1e-12: d['infos'].append("Error: My credit limit amount is too small to be stored in the database. Please enter another amount.") raise ValueError if acct.my_limit != my_limit: acct.my_limit = my_limit d['infos'].append("You have chosen to automatically accept a credit limit of up to %.2f from your partner." % acct.my_limit) except ValueError: d['infos'].append("My credit limit must be a number.") else: # set my limit to None = unlimited if acct.my_limit != None: # only change if not already None acct.my_limit = None d['infos'].append("You have chosen to automatically accept as high a credit limit as your partner offers you.") # modify my actual iou limit if necessary oldIOULimit = acct.iou_limit if acct.my_limit != None: acct.iou_limit = min(acct.my_limit, acct.get_partner_acct().partner_limit) else: # my_limit = unlimited acct.iou_limit = acct.get_partner_acct().partner_limit if acct.iou_limit != oldIOULimit: if acct.iou_limit == acct.my_limit: d['infos'].pop() # clear message about my maximum credit limit d['infos'].append("My credit limit changed to %.2f" % acct.iou_limit) acct.save() request.session['infos'] = d['infos'] return HttpResponseRedirect('.') # render account details page # *** probably just need to pass acct, and maybe units d['cur'] = acct.shared_data.currency d['acct'] = acct d['balance'] = acct.getBalance() #d['avgBalance'] = averageBalance(acct, 30) d['lowerLimit'] = acct.iou_limit d['upperLimit'] = acct.get_partner_acct().iou_limit d['displayBalance'] = d['balance'] #d['displayAvgBalance'] = d['avgBalance'] d['displayLowerLimit'] = d['lowerLimit'] d['displayUpperLimit'] = d['upperLimit'] if userNode.display_units_id and d['cur'].value: rate = d['cur'].value / userNode.display_units.value d['displayBalance'] *= rate #d['displayAvgBalance'] *= rate d['displayLowerLimit'] *= rate d['displayUpperLimit'] *= rate if d['cur'].value: d['convertibleUnits'] = CurrencyUnit.objects.filter(value__gt=0.0).order_by('long_name') d['closeable'] = (float('%.2f' % d['balance']) == 0.0) # get links for payments touching this account paymentTotal = 0.0 months = [] now = datetime.datetime.now() year = now.year month_num = now.month balance = acct.getBalance() highlight_row = False while True: # go month-by-month back to account creation month = {} links, interest, date = getLinksandInterestForMonth(acct, year, month_num) if year == now.year and month_num == now.month: interest += acct.getPresentInterest() else: month['date'] = date month['interest'] = interest month['balance'] = balance - paymentTotal if interest: month['highlight_row'] = highlight_row highlight_row = not highlight_row paymentTotal += interest months.append(month) entries = [] last_entry = None for link in links: link.balance = balance - paymentTotal paymentTotal += link.amount if last_entry and last_entry.path.payment_id == link.path.payment_id: last_entry.amount += link.amount else: link.highlight_row = highlight_row highlight_row = not highlight_row entries.append(link) last_entry = link month['links'] = entries if month_num == 1: month_num = 12 year = year -1 else: month_num -= 1 if datetime.datetime(year, month_num, 1) < acct.shared_data.created: break d['months'] = months d['paymentTotal'] = paymentTotal return render_to_response('accountDetail.html', d, context_instance=RequestContext(request))
def offerAction(request, offerId, action): """Accept or reject somebodys offer of credit""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) message = '' if Offer.objects.filter(pk=offerId).count() == 1: offer = Offer.objects.get(pk=offerId) else: # offer has been deleted since action link was displayed to user if action == 'accept' or action == 'reject': message = "Offer has been withdrawn and cannot be accepted or rejected." else: message = "Offer has already been acted on and cannot be withdrawn." request.session['infos'] = [message] return HttpResponseRedirect('/summary/') # check the offer is for the logged-in user if action is accept or reject if (action == 'accept' or action == 'reject') and \ offer.recipient_email not in getEmails(userNode): return HttpResponseRedirect('/summary/') if action == 'accept': initiator = offer.initiator # check for existing account between nodes # *** temporary until multiple accounts permitted if Account.objects.filter(owner=userNode, partner=initiator, shared_data__active=True).count() > 0: request.session['infos'] = [ "Account already exists with %s. Multiple accounts with the same neighbour are not permitted (yet)." % escape(initiator.name) ] offer.delete() return HttpResponseRedirect('/summary/') currencyId = offer.currency_id shared = SharedAccountData(currency_id=currencyId, balance=0.0, active=True, interest_rate=offer.interest_rate / 100.0) shared.save() acct1 = Account(owner=initiator, partner=userNode, shared_data=shared, balance_multiplier=1, iou_limit=0.0, partner_limit=offer.amount, name=userNode.name) acct1.save() acct2 = Account(owner=userNode, partner=initiator, shared_data=shared, balance_multiplier=-1, iou_limit=offer.amount, partner_limit=0.0, name=initiator.name) acct2.save() offer.delete() # shuffle node locations *** slow - consider doing as a regular script instead circle = routing.Circle() circle.shuffle(10 * Node.objects.count()) # send email informing initiator that offer was accepted t = template_loader.get_template('emailInvitationAccepted.txt') c = RequestContext(request, {'offer': offer, 'acct': acct1}) initiator_email = initiator.getPrimaryEmail() sendEmail("Invitation accepted", t.render(c), initiator_email) request.session['infos'] = [ "This is your new connection account with %s. Please take the time to give your account a meaningful name and assign your new neighbour a credit limit." % escape(initiator_email) ] return HttpResponseRedirect('/accounts/%d/' % acct2.id) elif action == 'reject': offer.delete() note = '' if request.method == 'POST': note = request.POST['note'] # send email informing initiator that offer was rejected t = template_loader.get_template('emailInvitationRejected.txt') c = RequestContext(request, {'offer': offer, 'note': note}) sendEmail("Your invitation was not accepted", t.render(c), offer.initiator.getPrimaryEmail()) message = "Offer from %s rejected." % escape( offer.initiator.getPrimaryEmail()) elif action == 'withdraw': if userNode != offer.initiator: return HttpResponseRedirect('/accounts/') offer.delete() message = "Offer to %s withdrawn." % offer.recipient_email if message: request.session['infos'] = [message] return HttpResponseRedirect('/accounts/')
def accountDetail(request, acctId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if Account.objects.filter(pk=acctId).count() > 0: acct = Account.objects.get(pk=acctId) else: # can't access accounts that aren't in database and active return HttpResponseRedirect('/accounts/') if acct.owner_id != userNode.id: # can't access accounts that aren't yours! return HttpResponseRedirect('/accounts/') d = {} d['infos'] = getSessionInfos(request) if request.method == 'POST': # modify account or set error if request.POST['name'] == '': d['infos'].append("Error: Account name can't be left blank.") elif acct.name != request.POST['name']: acct.name = request.POST['name'] d['infos'].append("Account name changed.") try: interest_rate = float( request.POST['interest']) / 100.0 # may raise ValueError shared_data = acct.shared_data if interest_rate <> shared_data.interest_rate: if shared_data.proposed_rate and interest_rate == shared_data.proposed_rate and userNode.id == shared_data.node_to_confirm_id: # take manually setting to otherly-proposed rate to be acceptance accept_proposed_rate(request, shared_data) d['infos'].append("Interest rate set to %s%%." % (interest_rate * 100.0)) else: info = propose_rate(request, shared_data, interest_rate, node_to_confirm=acct.partner) d['infos'].append(info) except ValueError: d['infos'].append("Interest rate must be a number.") try: # modify partner's max credit limit partner_limit = float(request.POST['partner_limit']) if partner_limit < 0.0: d['infos'].append( "Error: Partner's credit limit must be a positive number or zero. '%s' is invalid." % request.POST['partner_limit']) raise ValueError elif partner_limit >= 1e12: d['infos'].append( "Error: Partner limit amount is too large to be stored in the database. Please enter another amount." ) raise ValueError elif partner_limit <= 1e-12 and partner_limit != 0.0: d['infos'].append( "Error: Partner limit amount is too small to be stored in the database. Please enter another amount." ) raise ValueError if acct.partner_limit != partner_limit: acct.partner_limit = partner_limit d['infos'].append( "Partner has been offered a credit limit of up to %.2f." % acct.partner_limit) # modify partner's actual iou limit if necessary partnerAcct = acct.get_partner_acct() oldPartnerIOULimit = partnerAcct.iou_limit if partnerAcct.my_limit != None: partnerAcct.iou_limit = min(partnerAcct.my_limit, acct.partner_limit) else: partnerAcct.iou_limit = acct.partner_limit if partnerAcct.iou_limit != oldPartnerIOULimit: partnerAcct.save() if acct.partner_limit == partnerAcct.iou_limit: d['infos'].pop( ) # clear mesage about partner being offered credit d['infos'].append( "Partner's credit limit changed to %.2f." % partnerAcct.iou_limit) except ValueError: d['infos'].append("Partner's credit limit must be a number.") if request.POST['my_limit'] != '': try: # modify my max credit limit my_limit = float(request.POST['my_limit']) if my_limit < 0.0: d['infos'].append( "Error: My credit limit must be a positive number or zero. '%s' is invalid." % request.POST['my_limit']) raise ValueError elif my_limit >= 1e12: d['infos'].append( "Error: My credit limit amount is too large to be stored in the database. Please enter another amount." ) raise ValueError elif my_limit <= 1e-12: d['infos'].append( "Error: My credit limit amount is too small to be stored in the database. Please enter another amount." ) raise ValueError if acct.my_limit != my_limit: acct.my_limit = my_limit d['infos'].append( "You have chosen to automatically accept a credit limit of up to %.2f from your partner." % acct.my_limit) except ValueError: d['infos'].append("My credit limit must be a number.") else: # set my limit to None = unlimited if acct.my_limit != None: # only change if not already None acct.my_limit = None d['infos'].append( "You have chosen to automatically accept as high a credit limit as your partner offers you." ) # modify my actual iou limit if necessary oldIOULimit = acct.iou_limit if acct.my_limit != None: acct.iou_limit = min(acct.my_limit, acct.get_partner_acct().partner_limit) else: # my_limit = unlimited acct.iou_limit = acct.get_partner_acct().partner_limit if acct.iou_limit != oldIOULimit: if acct.iou_limit == acct.my_limit: d['infos'].pop() # clear message about my maximum credit limit d['infos'].append("My credit limit changed to %.2f" % acct.iou_limit) acct.save() request.session['infos'] = d['infos'] return HttpResponseRedirect('.') # render account details page # *** probably just need to pass acct, and maybe units d['cur'] = acct.shared_data.currency d['acct'] = acct d['balance'] = acct.getBalance() #d['avgBalance'] = averageBalance(acct, 30) d['lowerLimit'] = acct.iou_limit d['upperLimit'] = acct.get_partner_acct().iou_limit d['displayBalance'] = d['balance'] #d['displayAvgBalance'] = d['avgBalance'] d['displayLowerLimit'] = d['lowerLimit'] d['displayUpperLimit'] = d['upperLimit'] if userNode.display_units_id and d['cur'].value: rate = d['cur'].value / userNode.display_units.value d['displayBalance'] *= rate #d['displayAvgBalance'] *= rate d['displayLowerLimit'] *= rate d['displayUpperLimit'] *= rate if d['cur'].value: d['convertibleUnits'] = CurrencyUnit.objects.filter( value__gt=0.0).order_by('long_name') d['closeable'] = (float('%.2f' % d['balance']) == 0.0) # get links for payments touching this account paymentTotal = 0.0 months = [] now = datetime.datetime.now() year = now.year month_num = now.month balance = acct.getBalance() highlight_row = False while True: # go month-by-month back to account creation month = {} links, interest, date = getLinksandInterestForMonth( acct, year, month_num) if year == now.year and month_num == now.month: interest += acct.getPresentInterest() else: month['date'] = date month['interest'] = interest month['balance'] = balance - paymentTotal if interest: month['highlight_row'] = highlight_row highlight_row = not highlight_row paymentTotal += interest months.append(month) entries = [] last_entry = None for link in links: link.balance = balance - paymentTotal paymentTotal += link.amount if last_entry and last_entry.path.payment_id == link.path.payment_id: last_entry.amount += link.amount else: link.highlight_row = highlight_row highlight_row = not highlight_row entries.append(link) last_entry = link month['links'] = entries if month_num == 1: month_num = 12 year = year - 1 else: month_num -= 1 if datetime.datetime(year, month_num, 1) < acct.shared_data.created: break d['months'] = months d['paymentTotal'] = paymentTotal return render_to_response('accountDetail.html', d, context_instance=RequestContext(request))
def offer(request): """offer credit to multiple nodes""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not request.POST: return HttpResponseRedirect('/summary/') if request.POST[ 'phase'] == 'make_offers': # check if offers are finalized, or if we're just listing emails and the form # extract info from POST dictionary rows = [] for i in range(0, int(request.POST['count'])): row = Struct() row.email = request.POST['email_%d' % i] # already parsed as email addresses row.amount = request.POST['amount_%d' % i] row.currency = request.POST['currency_%d' % i] row.interest = request.POST['interest_%d' % i] rows.append(row) # find errors in entries error = False for row in rows: # ensure no double-offers or double-accounts emailList, errors = validateOfferEmails([row.email], userNode) if not emailList: # if row.email is in list, then offer is valid error = True row.error = errors[0] # only show first error continue # only one error per row # validate amounts as numbers try: row.amount = float(row.amount) if row.amount <= 0.0: raise ValueError except ValueError: error = True row.error = "Credit limit must be a number greater than zero." else: if row.amount >= 10**12: error = True row.error = "Amount is too large to be stored in the database. Please enter another amount." elif row.amount <= 10**-12: error = True row.error = "Amount is too small to be stored in the database. Please enter another amount." if error: continue # only one error per row try: row.interest = float(row.interest) except ValueError: error = True row.error = "Interest rate must be a number." note = request.POST['note'] if note == '': note = None if error: # show form again with errors and values still there (in row objects) d = {'rows': rows, 'count': len(rows)} d['infos'] = [ "One of more of your entries had errors. Please make corrections below." ] else: # create and save offers, and send out emails infos = [] infos.append( 'The system has recorded your offers of credit and is sending out invitation emails. <a href="/accounts/#offers">Click here to see your offers.</a>' ) #infos.append("If the invitation does not arrive, it may be in the destination's spam folder...") for row in rows: currency = CurrencyUnit.objects.get(short_name=row.currency) offer = Offer(initiator=userNode, recipient_email=row.email, amount=row.amount, currency=currency, interest_rate=row.interest) offer.save() # send out email t = template_loader.get_template('emailInvitation.txt') c = RequestContext( request, { 'row': row, 'userNode': userNode, 'currency': currency, 'note': note }) sender = '"%s" <%s>' % (userNode.name, userNode.getPrimaryEmail()) if not sendEmail('Invitation to Ripple', t.render(c), row.email, sender=sender, attempts=2, includeServiceName=False): infos.append( "An email could not be sent to %s, but your offer has been stored. Please contact %s manually." % (escape(row.email), escape(row.email))) request.session['infos'] = infos return HttpResponseRedirect('/accounts/') else: # else, list emails + form emails = parseEmails(request.POST['emailText']) emails, infos = validateOfferEmails(emails, userNode) if emails == [] and infos == []: infos.append("You didn't enter any valid email addresses.") rows = [] for email in emails: row = Struct() row.email = email row.currency = None row.interest = 0 rows.append(row) d = {'infos': infos, 'rows': rows, 'count': len(rows)} d['currencies'] = CurrencyUnit.objects.order_by('long_name') return render_to_response('offer.html', d, context_instance=RequestContext(request))
def paymentDetail(request, pmtId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if Payment.objects.filter(pk=pmtId).count() == 0: return HttpResponseRedirect('/summary') pmt = Payment.objects.get(pk=pmtId) if pmt.status != 'OK': return HttpResponseRedirect('/summary') d = {} # get any messages from session d['infos'] = getSessionInfos(request) # get every payment link that touches this user inLinks = PaymentLink.objects.filter(path__payment=pmtId, payer_account__partner=userNode) outLinks = PaymentLink.objects.filter(path__payment=pmtId, payer_account__owner=userNode) if pmt.payer_id == userNode.id: assert outLinks, "User %d is payer for payment %d, but has no outgoing payment links." % ( userNode.id, pmt.id) d['outPayment'] = True elif pmt.recipient_id == userNode.id: assert inLinks, "User is %d is recipient for payment %d, but has no incoming payment links." % ( userNode.id, pmt.id) d['inPayment'] = True elif inLinks and outLinks: d['thruPayment'] = True else: # user should have no links at all assert not inLinks and not outLinks, "User %d is an intermediary for payment %d, but does not have both incoming and outgoing links." % ( userNode.id, pmt.id) return HttpResponseRedirect('/summary') inAccts = [] for link in inLinks: acct = link.payer_account.get_partner_acct() if acct in inAccts: # fetch copy we already have and add to numbers acct = inAccts[inAccts.index(acct)] acct.inEntry += link.amount else: acct.inEntry = link.amount inAccts.append(acct) outAccts = [] for link in outLinks: acct = link.payer_account if acct in outAccts: # fetch copy we already have and add to numbers acct = outAccts[outAccts.index(acct)] acct.outEntry += link.amount else: acct.outEntry = link.amount outAccts.append(acct) d['pmt'] = pmt d['inAccts'] = inAccts d['outAccts'] = outAccts return render_to_response('paymentDetail.html', d, context_instance=RequestContext(request))
def accountAction(request, acctId, action): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/accounts/%s/' % acctId) if request.method == 'POST': if request.POST['submit'] == 'Cancel': return HttpResponseRedirect('/accounts/%s/' % acctId) if Account.objects.filter(pk=acctId).count() == 0: return HttpResponseRedirect('/accounts/') acct = Account.objects.get(pk=acctId) if acct.owner_id != userNode.id: return HttpResponseRedirect('/accounts/') if action == 'close': sharedData = acct.shared_data if abs(sharedData.balance) >= 0.005: request.session['infos'] = [ "Account balance must be zero before it can be closed." ] return HttpResponseRedirect('/accounts/%d/' % acct.id) # to be sure a transaction doesn't hit this account before it gets # deactivated, set limits to zero, save, and then check for zero balance again myIOULimit = acct.iou_limit partnerAcct = acct.get_partner_acct() partnerIOULimit = partnerAcct.iou_limit acct.iou_limit = 0.0 acct.save() partnerAcct.iou_limit = 0.0 partnerAcct.save() if abs(sharedData.balance) >= 0.005: request.session['infos'] = [ "Account balance must be zero before it can be closed." ] # restore limits on accts acct.iou_limit = myIOULimit acct.save() partnerAcct.iou_limit = partnerIOULimit partnerAcct.save() return HttpResponseRedirect('/accounts/%d/' % acct.id) # now it's impossible for a transaction to change the balance, and the balance is zero, so: sharedData.active = False sharedData.save() # notify user request.session['infos'] = [ "Account %d (%s) closed." % (sharedData.id, acct.name) ] # notify partner by email note = '' if request.method == 'POST': note = request.POST['note'] t = template_loader.get_template('emailAcctClosed.txt') c = RequestContext(request, {'acct': partnerAcct, 'note': note}) sendEmail("One of your accounts has been closed", t.render(c), acct.partner.getPrimaryEmail()) pass return HttpResponseRedirect('/accounts/')
def paymentForm(request, pmtId=None, otherUserId=None, is_request=False): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) errors = [] d = {} if request.method == 'POST': # get post data and attempt payment email = request.POST['email'] d['email'] = email # for maintaining form value in case of error if EmailAddr.objects.filter(email=email, confirmed=True).count() == 0: errors.append("Email '%s' does not belong to any Ripple user," "or has not yet been confirmed." % escape(email)) else: emailAddr = EmailAddr.objects.get(email=email) otherUser = emailAddr.node if otherUser == userNode: action = is_request and 'request payment from' or 'pay' errors.append("You can't %s yourself." % action) amountStr = request.POST['amount'] d['amount'] = amountStr try: amount = float(amountStr) if amount <= 0.0: raise Exception except: errors.append( "Amount must be a positive number. '%s' is not valid." % escape(amountStr)) currencyName = request.POST[ 'currency'] # this should be OK, since it's from a generated list currency = CurrencyUnit.objects.get(short_name=currencyName) d['selectedUnitId'] = currency.id d['description'] = request.POST['description'] if not errors: # check for enough credit available = availableCredit(userNode, currency, incoming=is_request) if available < amount: action = is_request and 'request' or 'make' errors.append( "You do not have enough available credit to %s payment. " "Available: %s %.2f." % (action, currency.short_name, available)) if availableCredit(otherUser, currency, incoming=not is_request) < amount: if is_request: pass # Other user might get more credit to fulfill request else: errors.append( "Your intended recipient has not offered enough credit " "to receive your payment.") pmt = None if request.POST['pmtId'] != 'None': pmt = Payment.objects.get(pk=request.POST['pmtId']) # Check if user can edit if (not is_request and pmt.payer_id != userNode.id) or \ (is_request and pmt.recipient_id != userNode.id): return HttpResponseRedirect('/payments/') if pmt.status == 'OK': # can't edit completed payment d = { 'message': 'Payment has already been completed.', 'link': '/payments/' } return render_to_response( 'error.html', d, context_instance=RequestContext(request)) d['pmtId'] = pmt.id # in case of errors, re-post pmtId if not errors: payer = is_request and otherUser or userNode recipient = is_request and userNode or otherUser if pmt is None: pmt = Payment() # Create new Payment. pmt.payer = payer pmt.payer_email = is_request and email or payer.getPrimaryEmail() pmt.recipient = recipient pmt.recipient_email = is_request and recipient.getPrimaryEmail( ) or email pmt.amount = amount pmt.currency = currency pmt.date = datetime.now() pmt.status = is_request and 'RQ' or 'PE' pmt.description = d['description'] pmt.save() if is_request: # Send payment request email t = template_loader.get_template('emailPmtRequestReceived.txt') c = RequestContext(request, {'req': pmt}) sendEmail("Payment request notification", t.render(c), pmt.payer_email) request.session['infos'] = [ "Your payment request has been recorded " "and a notification email sent to the payer." ] return HttpResponseRedirect('/payments/') else: # Go to payment confirmation page return HttpResponseRedirect('/payments/%s/confirm/' % pmt.id) # present make payment form d['infos'] = errors if otherUserId: # paying a specific user - URL = payUser/id/ if Node.objects.filter( pk=otherUserId).count() > 0 and userNode.id != otherUserId: d['email'] = Node.objects.get(pk=otherUserId).getPrimaryEmail() if pmtId: # editing a pending payment from DB - URL = paymentForm/id/ if Payment.objects.filter(pk=pmtId).count() > 0: pmt = Payment.objects.get(pk=pmtId) if pmt.status == 'OK': # can't edit completed payment d = { 'message': 'Payment has already been completed.', 'link': '/payments/' } return render_to_response( 'error.html', d, context_instance=RequestContext(request)) d['email'] = is_request and pmt.payer_email or pmt.recipient_email d['amount'] = pmt.amount d['selectedUnitId'] = pmt.currency_id d['description'] = pmt.description d['pmtId'] = pmtId d['paymentUnits'] = makeUnitsList(userNode) # set selected units to account units, if paying account partner, or to default units if not d.has_key('selectedUnitId'): if otherUserId and userNode.account_set.filter( partner__pk=otherUserId).count() > 0: acct = userNode.account_set.filter(partner__pk=otherUserId)[0] d['selectedUnitId'] = acct.shared_data.currency_id else: d['selectedUnitId'] = userNode.display_units_id d['is_request'] = is_request return render_to_response('paymentForm.html', d, context_instance=RequestContext(request))