Beispiel #1
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))
Beispiel #2
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))
Beispiel #3
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))
Beispiel #4
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))
Beispiel #5
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))
Beispiel #6
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))
Beispiel #7
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))
Beispiel #8
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))
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))
Beispiel #10
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))
Beispiel #11
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))
Beispiel #12
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))
Beispiel #13
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))
Beispiel #14
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))