Files
mortgage/mortgage_template.py
2018-12-23 01:05:36 -05:00

300 lines
11 KiB
Python

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
def getPaymentHistory(datastore, loan, asOfDate):
pass
def getAmortization(datastore, loan, paymentHistory):
pass
def loadLoanInformation(filename):
datastore = getDatastore(filename)
loanModel = {}
loanModel['loan'] = getLoan(datastore)
loanModel['lender'] = getLender(datastore)
loanModel['borrower'] = getBorrower(datastore)
loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate)
loanModel['futureAmortization'] = getAmortization(datastore, loan, loanModel.paymentHistory)
return loanModel
def getLoan(datastore):
# read in the loan profile information
loan = {}
annual_rate = Decimal(datastore['loan.interest rate']) / 100
daily_interest_rate = annual_rate / 360
principal = Decimal(datastore["loan.principal"]).quantize(Decimal("1.00"))
periods_per_year = Decimal(datastore["loan.periods per year"])
total_periods = Decimal(datastore["loan.periods"])
payment_day_of_month = int(datastore['loan.payment day of month'])
if "monthly_payment" in datastore:
monthly_payment = Decimal(datastore["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['loan.account_number'] = '123456789'
loan['principal'] = principal
loan['term'] = total_periods
loan['annual_rate'] = annual_rate
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'
return loan
def getLender(datastore):
lender = {}
# to be replaced with real loading code
lender['name'] = 'Rivanna Graphite Investments, LLC'
lender['phone'] = '703.343.0782'
lender['address'] = '743 Madison St NW'
lender['city'] = 'Washington'
lender['state'] = 'DC'
lender['zip'] = '20011'
return getLender
def getBorrower(datastore):
borrower = {}
# to be replaced with real loading code
borrower['name'] = 'Bear Houses, LLC'
borrower['address'] = '123 Any Street'
borrower['city'] = 'Alltown'
borrower['state'] = 'VA'
borrower['zip'] = '11111'
return 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 calculateLoanAmortization(loanModel):
return loanModel
def transformTemplate(template_fileName, loanModel):
# template_filename = "statement.txt.jinja"
# setup jinja for creating the statement
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template(template_fileName)
report = template.render(original_principal_balance=loanModel.principal, future_payments=loanModel.future_payments,
past_payments=loanModel.past_payments, balloon_payment=loanModel.remaining_principal,
total_interest_paid_to_date=loanModel.total_interest, statement=loanModel.statement)
return report
def generatePDFStatement():
template_filename = "statement.pdf.jinja"
pass
def generateHTMLStatement():
pass
def generateTextStatement():
pass
def generateEmail(from_address, to_address, subject, body, attachment):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = from_address
msg['To'] = to_address
msg.attach(MIMEText(body))
part = MIMEBase('application', "octet-stream")
part.set_payload(attachment)
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"')
msg.attach(part)
return msg
def main():
# this program reads the json file describing a mortgage and calculate the actual and projected amortization
# the first payment in the file should be the interest paid at closing
# the interest at closing covers interest that would be incurred from the date of closing until the first day
# that will be covered by the first loan payment
# i.e., the first loan payment is expected a month and a half after closing, the first two weeks interest is due
# at closing. The first payment will incur interest from one month before the bill is due.
from_address = 'jkent3rd@gmail.com'
passwd = "pvyrbcnzrjoizprn"
subject = "Mortgage or Loan Statement"
to_address = 'jkent3rd@gmail.com'
body = 'This email contains a PDF of your mortgage statement.'
# read in the file
# filename = "./10Kloan.txt"
# filename = "./10Kloan.test.txt"
# filename = "./dadmortgage.txt"
# filename = "./brendamortgage.txt"
filename = "./greenfield_mortgage.txt"
template_filename = "statement.pdf.jinja"
loanInformation = loadLoanInformation(filename)
loanModel = calculateLoanAmortization(loanInformation)
# read in the statement information
statement, lender, borrower, loan = {}, {}, {}, {}
lender = loanModel.lender
borrower = loanModel.borrower
past_payments = loanModel.past_payments
future_payments = loanModel.future_payments
statement['title'] = "Mortgage Statement - 185 James River Rd"
statement['date'] = "Today"
statement['lender'] = lender
statement['borrower'] = borrower
statement['loan'] = loan
# loop over the payments and calculate the actual amortization
actual_payments = loanModel["payments"]
remaining_principal = loanModel.principal
payment_day_of_month = loanInformation.payment_day_of_month
daily_interest_rate = loanInformation.daily_interest_rate
total_periods = loanInformation.total_periods
annual_interest = 0
total_interest = 0
interest_paid_through_date = datetime.strptime(loanModel["start_interest_date"], "%Y-%m-%d").date()
next_payment_date = datetime.strptime(loanModel["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)
old_bill_date = next_bill_date
payment_number = 1
current_year = next_bill_date.year
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
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['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)
# 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_interest
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)
report = transformTemplate('', loanModel)
# create pdf
class MyFPDF(FPDF, HTMLMixin):
pass
pdf = MyFPDF()
pdf.set_font(family='Arial', size=12)
pdf.add_page()
pdf.write_html(report)
# pdf.output(name='test.pdf', dest='F')
attachment = pdf.output(dest='S')
# send email
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(from_address, passwd)
email = generateEmail(from_address, to_address, subject, body, attachment)
server.sendmail(from_address, to_address, email.as_string())
server.close()
except:
print "Couldn't send email, dumping statement to file."
pdf.output(name='test.pdf', dest='F')
quit()
if __name__ == '__main__':
main()