From a30486970889feabe9716f80d91fd6fd0daa6abb Mon Sep 17 00:00:00 2001
From: JohnKent
Date: Sun, 7 Jul 2019 14:59:33 -0400
Subject: [PATCH] At this state, it produces a nice web-based viewer of the
loan files including the payment history and future amortization.
---
templates/main.html | 159 +++++++++++++++++++++++++++++
web.py | 244 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 403 insertions(+)
create mode 100644 templates/main.html
create mode 100644 web.py
diff --git a/templates/main.html b/templates/main.html
new file mode 100644
index 0000000..674a873
--- /dev/null
+++ b/templates/main.html
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+ Loan Management
+
+
+Web Mortgage Manager
+
+
+
+
+
+
+
+
+
+ | Loan History |
+
+
+ | #
+ | Due Date |
+ Date Paid |
+ Days Interest |
+ Payment Amt |
+ Principal Pmt |
+ Interest Pmt |
+ New Balance |
+
+
+
+ {% for item in model.past_payments %}
+
+ | {{ item.payment_number }} |
+ {{ item.bill_date }} |
+ {{ item.payment_date }} |
+ {{ item.days_of_interest }} |
+ {{ "$%.2f"|format(item.payment_amount) }} |
+ {{ "$%.2f"|format(item.principal_payment) }} |
+ {{ "$%.2f"|format(item.interest_payment) }} |
+ {{ "$%.2f"|format(item.new_balance) }} |
+
+ {% if item.month == 12 or loop.last %}
+ | Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}. |
+ {% endif %}
+ {% endfor %}
+ | Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}. |
+
+
+
+
+
+
+
+ | Remaining Amortization |
+
+
+ | # |
+ Due Date |
+ Days Interest |
+ Payment Amt |
+ Principal Pmt |
+ Interest Pmt |
+ Principal Balance |
+
+
+
+ {% for item in model.future_payments %}
+ | {{ item.payment_number }} |
+ {{ item.payment_date }} |
+ {{ item.days_of_interest }} |
+ {{ "$%.2f"|format(item.payment_amount) }} |
+ {{ "$%.2f"|format(item.principal_payment) }} |
+ {{ "$%.2f"|format(item.interest_payment) }} |
+ {{ "$%.2f"|format(item.new_balance) }} |
+
+ {% endfor %}
+ Balloon Payment Due: {{ "$%.2f"|format(model.balloon_payment) }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web.py b/web.py
new file mode 100644
index 0000000..672499d
--- /dev/null
+++ b/web.py
@@ -0,0 +1,244 @@
+from flask import Flask, render_template, request, redirect, url_for
+import json
+from decimal import *
+from datetime import *
+from jinja2 import Environment, FileSystemLoader
+from fpdf import FPDF, HTMLMixin
+import smtplib
+
+from email.MIMEMultipart import MIMEMultipart
+from email.MIMEText import MIMEText
+from email.MIMEBase import MIMEBase
+from email import Encoders
+
+app = Flask(__name__)
+
+@app.route('/')
+def hello():
+
+ loans = []
+ loans.append( addLoan("1 Greenfield Lane Mortgage", "greenfield_mortgage.json" ) )
+ loans.append( addLoan("2 Bear Houses LLC Loan", "9Kloan.json" ) )
+ loans.append( addLoan("3 Brenda Mortgage", "brendamortgage.json" ) )
+ loans.append( addLoan("4 Dad Mortgage", "dadmortgage.json") )
+
+ if 'loan' in request.args:
+ filename = request.args["loan"]
+ else:
+ return redirect('/?loan=' + loans[0]['filename'])
+
+ loan = loadLoanInformation('/Users/john/PycharmProjects/mortgage/' + filename)
+ amortizeLoan(loan)
+
+ return render_template('main.html', filename=filename, loans=loans, model=loan)
+
+@app.route('/update_file')
+def update_file():
+ return
+
+@app.route('/send_statement', methods=['POST'])
+def send_statement():
+ loan = request.form["loan"]
+ redirect( '/?loan=' + loan )
+
+def addLoan(loanName, fileName):
+ x = {}
+ x['name'] = loanName
+ x['filename'] = fileName
+ return x
+
+'''from old code'''
+def getStatementHeader(datastore):
+ return datastore['header']
+
+
+def getEmailInformation(datastore):
+ return datastore['email']
+
+
+def loadLoanInformation(filename):
+ datastore = getDatastore(filename)
+
+ loanModel = {}
+ loanModel['datastore'] = datastore
+ loanModel['email'] = getEmailInformation(datastore)
+ loanModel['parameters'] = getLoanParameters(datastore)
+ loanModel['lender'] = getLender(datastore)
+ loanModel['borrower'] = getBorrower(datastore)
+ loanModel['header'] = getStatementHeader(datastore)
+ return loanModel
+
+
+def getLoanParameters(datastore):
+ # read in the loan profile information
+ loan = datastore['parameters']
+
+ annual_rate = Decimal(loan['interest_rate']) / 100
+ daily_interest_rate = annual_rate / 360
+ principal = Decimal(loan["principal"]).quantize(Decimal("1.00"))
+ periods_per_year = Decimal(loan["periods_per_year"])
+ total_periods = Decimal(loan["periods"])
+ payment_day_of_month = int(loan['payment_day_of_month'])
+
+ if "monthly_payment" in loan:
+ monthly_payment = Decimal(loan["monthly_payment"]).quantize(Decimal("1.00"))
+ else:
+ # calculate expected monthly payment
+ periodic_rate = annual_rate / periods_per_year
+ discount_factor = (((1 + periodic_rate) ** total_periods) - 1) / (
+ periodic_rate * ((1 + periodic_rate) ** total_periods))
+ monthly_payment = (principal / discount_factor).quantize(Decimal("1.00"))
+
+ loan['principal'] = principal # standardizes the format
+ loan['annual_rate'] = annual_rate # standardizes the format
+ loan['daily_interest_rate'] = daily_interest_rate
+ loan['rate'] = '' + (annual_rate * 100).__str__() + '%'
+ loan['next_payment_amt'] = 0
+ loan['next_payment_date'] = '12/12/12'
+ loan['total_periods'] = total_periods
+ loan['monthly_payment'] = monthly_payment
+ datastore['parameters'] = loan
+ return loan
+
+
+def getLender(datastore):
+ return datastore['lender']
+
+
+def getBorrower(datastore):
+ return datastore['borrower']
+
+
+def getDatastore(filename=None):
+ try:
+ if filename:
+ with open(filename, 'r') as f:
+ datastore = json.load(f)
+
+ except Exception as e:
+ print "An error occurred opening your loan file '%s'. " % filename
+ print "The Exception:"
+ print e.__repr__()
+ quit()
+
+ return datastore
+
+
+def amortizeLoan(loan):
+ # loop over the payments and calculate the actual amortization
+ monthly_payment = loan["parameters"]["monthly_payment"]
+
+ actual_payments = loan["datastore"]["payments"]
+
+ remaining_principal = loan["parameters"]["principal"]
+ payment_day_of_month = int(loan["parameters"]["payment_day_of_month"])
+ daily_interest_rate = loan["parameters"]["daily_interest_rate"]
+ total_periods = loan["parameters"]["total_periods"]
+ interest_paid_through_date = datetime.strptime(loan["parameters"]["start_interest_date"], "%Y-%m-%d").date()
+ next_payment_date = datetime.strptime(loan["parameters"]["first_payment_month"], '%Y-%m-%d').date()
+ next_bill_date = date(year=next_payment_date.year, month=next_payment_date.month, day=payment_day_of_month)
+
+ payment_number = 1
+ annual_interest = 0
+ total_interest = 0
+ old_bill_date = next_bill_date
+ current_year = next_bill_date.year
+
+ past_payments = []
+ future_payments = []
+
+ for payment in actual_payments:
+ payment_date = datetime.strptime((payment[0]), '%Y-%m-%d').date()
+ payment_amount = Decimal(payment[1]).quantize(Decimal("1.00"))
+ days_since_last_payment = (payment_date - interest_paid_through_date).days
+
+ #check for out of order payments, generally a sign of a data entry problem, especially years
+ if days_since_last_payment < 0:
+ print "Payment Number %s appears out of order. The payment date '%s' is before the previous payment on '%s'." \
+ % (payment_number, payment_date, interest_paid_through_date)
+ quit()
+
+ new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00"))
+ new_principal = payment_amount - new_interest
+ interest_paid_through_date = payment_date
+ total_interest = total_interest + new_interest
+ annual_interest = annual_interest + new_interest
+ remaining_principal = remaining_principal - new_principal
+
+
+ # create the payment record for the template to render
+ payment_record = {}
+ payment_record['year']=next_bill_date.year
+ payment_record['month']=next_bill_date.month
+ payment_record['payment_number'] = payment_number
+ payment_record['bill_date'] = next_bill_date
+ payment_record['payment_date'] = payment_date
+ payment_record['days_of_interest'] = days_since_last_payment
+ payment_record['payment_amount'] = payment_amount
+ payment_record['principal_payment'] = payment_amount - new_interest
+ payment_record['interest_payment'] = new_interest
+ payment_record['new_balance'] = remaining_principal
+ payment_record['interest_to_date'] = total_interest
+ payment_record['annual_interest_to_date'] = annual_interest
+ past_payments.append(payment_record)
+
+ payment_number = payment_number + 1
+ old_bill_date = next_bill_date
+
+ if old_bill_date.month < 12:
+ next_bill_date = date(year=old_bill_date.year, month=old_bill_date.month + 1, day=payment_day_of_month)
+ else:
+ annual_interest = Decimal("0.00")
+ next_bill_date = date(year=old_bill_date.year + 1, month=1, day = payment_day_of_month)
+
+ loan["total_interest_paid_to_date"] = total_interest
+ loan["parameters"]["next_due_date"] = next_bill_date
+
+ if (remaining_principal < monthly_payment):
+ loan["parameters"]["next_payment_amt"] = remaining_principal
+ else:
+ loan["parameters"]["next_payment_amt"] = monthly_payment
+
+ # loop over remaining scheduled payments and present estimated amortization
+ while (payment_number <= total_periods) and (remaining_principal > 0):
+ days_since_last_payment = (next_bill_date - interest_paid_through_date).days
+ new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00"))
+
+ # make sure the last payment isn't too much
+ if new_interest + remaining_principal < monthly_payment:
+ monthly_payment = new_interest + remaining_principal
+
+ new_principal = monthly_payment - new_interest
+
+ remaining_principal = remaining_principal - new_principal
+
+ interest_paid_through_date = next_bill_date
+
+ # complete the future payment amortization record
+ future_payment_record = {}
+ future_payment_record['payment_number'] = payment_number
+ future_payment_record['payment_date'] = next_bill_date
+ future_payment_record['days_of_interest'] = days_since_last_payment
+ future_payment_record['payment_amount'] = monthly_payment
+ future_payment_record['principal_payment'] = new_principal
+ future_payment_record['interest_payment'] = new_interest
+ future_payment_record['new_balance'] = remaining_principal
+ future_payments.append(future_payment_record)
+
+ payment_number = payment_number + 1
+ old_bill_date = next_bill_date
+ if old_bill_date.month < 12:
+ next_bill_date = date(year=old_bill_date.year, month=old_bill_date.month + 1, day=payment_day_of_month)
+ else:
+ next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month)
+
+
+ loan["balloon_payment"] = remaining_principal
+ loan["past_payments"] = past_payments
+ loan["future_payments"] = future_payments
+ return
+
+
+if __name__ == '__main__':
+ app.debug = True
+ app.run()
\ No newline at end of file