示例#1
0
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/')
示例#2
0
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/')
示例#3
0
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))
示例#4
0
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/')
示例#5
0
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))
示例#6
0
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))
示例#7
0
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))
示例#8
0
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
示例#9
0
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))
示例#10
0
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))
示例#11
0
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))
示例#12
0
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))
示例#13
0
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))
示例#14
0
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))
示例#15
0
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)
示例#16
0
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))
示例#17
0
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))
示例#18
0
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)
示例#19
0
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
示例#20
0
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))
示例#21
0
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))
示例#22
0
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))
示例#23
0
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))
示例#24
0
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))
示例#25
0
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))
示例#26
0
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))
示例#27
0
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/')
示例#28
0
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))
示例#29
0
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/')
示例#30
0
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))
示例#31
0
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))
示例#32
0
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))
示例#33
0
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/')
示例#34
0
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))