Web version updated to allow sending emails.
This commit is contained in:
18
templates/add.html
Normal file
18
templates/add.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||||
|
<link rel="stylesheet" href="/resources/demos/style.css">
|
||||||
|
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||||
|
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||||
|
<script>
|
||||||
|
$( function() { $( "#tabs" ).tabs(); } );
|
||||||
|
</script>
|
||||||
|
<title>Loan Management</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="header" align="center"><font face="Arial" size=+2 >Web Mortgage Manager</font></p>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/?loan={{filename}}">Return to Main Screen</a>.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
templates/email.html
Normal file
19
templates/email.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||||
|
<link rel="stylesheet" href="/resources/demos/style.css">
|
||||||
|
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||||
|
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||||
|
<script>
|
||||||
|
$( function() { $( "#tabs" ).tabs(); } );
|
||||||
|
</script>
|
||||||
|
<title>Loan Management</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="header" align="center"><font face="Arial" size=+2 >Web Mortgage Manager</font></p>
|
||||||
|
|
||||||
|
<p> The email has been sent.</p>
|
||||||
|
<a href="/?loan={{filename}}">Return to Main Screen</a>.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<title>Loan Management</title>
|
<title>Loan Management</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p align="center">Web Mortgage Manager</p>
|
<p id="header" align="center"><font face="Arial" size=+2 >Web Mortgage Manager</font></p>
|
||||||
<form>
|
<form>
|
||||||
<table><tr>
|
<table><tr>
|
||||||
<td>Loan:</td>
|
<td>Loan:</td>
|
||||||
@@ -131,21 +131,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="email">
|
<div id="email">
|
||||||
<form name="Send Statement" method="post" action="/send_statement">
|
<form name="Send Statement" method="post" action="/send_statement">
|
||||||
<table>
|
<table align="center" border='1px' width="75%">
|
||||||
<tr><td colspan="2">Generate and Send Statement</td></tr>
|
<tr><td align="center" bgcolor="darkgrey">Generate and Send Statement</td></tr>
|
||||||
<tr><td>Send From:</td><td>{{model.email.from_address}}</td></tr>
|
<tr><td>Send From: {{model.email.from_address}}<br/>Send To: {{model.email.to_address}}</td></tr>
|
||||||
<tr><td>Send To:</td><td>{{model.email.to_address}}</td></tr>
|
<tr><td bgcolor="beige">Subject:</td></tr>
|
||||||
<tr><td>Topic:</td><td><input type="text" name="title" value="{{model.email.subject}}"></td></tr>
|
<tr><td align="center"><textarea type="text" cols="120" name="subject">{{model.email.subject}}</textarea></td></tr>
|
||||||
<tr><td>Message Body:</td><td><input type="textarea" name="message" value="{{model.email.body}}"></td></tr>
|
<tr><td bgcolor="beige">Message:</td></tr>
|
||||||
<tr><td>Send Statement As:</td><td><select name="style">
|
<tr><td align="center"><textarea rows="8" cols="120" name="message">{{model.email.body}}</textarea></td></tr>
|
||||||
<option value="attach">Attachment</option>
|
<tr><td bgcolor="beige">Send Statement As:</td></tr>
|
||||||
<option value="embed">Embedded</option>
|
<tr><td>
|
||||||
</select></td></tr>
|
<input type="checkbox" name="html" value="html"/> HTML
|
||||||
<tr><td>Include Future Amortization</td>
|
<input type="checkbox" name="pdf" value="pdf"/> PDF
|
||||||
<td><select name="amort">
|
<input type="checkbox" name="text" value="text" /> Plain Text
|
||||||
<option value="yes">Yes</option>
|
</td></tr>
|
||||||
<option value="no">No</option>
|
<tr><td bgcolor="beige">Include Future Amortization</td></tr>
|
||||||
</select></td></tr>
|
<tr><td>
|
||||||
|
<input name="amortization" type="radio" value="yes" checked>Yes
|
||||||
|
<input name="amortization" type="radio" value="no">No
|
||||||
|
</td></tr>
|
||||||
<tr><td><button>Send Statement</button></td></tr>
|
<tr><td><button>Send Statement</button></td></tr>
|
||||||
</table>
|
</table>
|
||||||
<input hidden="loan" name='loan' value="{{filename}}" />
|
<input hidden="loan" name='loan' value="{{filename}}" />
|
||||||
|
|||||||
84
templates/statement.html.jinja
Normal file
84
templates/statement.html.jinja
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<font face='arial' size='14'><p align='center'>{{ model.header.title }}</p></font>
|
||||||
|
<font face='arial' size='10'><p align='center'>{{ model.lender.name }}</p></font>
|
||||||
|
<font face='arial' size='8'>
|
||||||
|
<p align='center'>{{ model.lender.phone }} - {{ model.lender.address }} -
|
||||||
|
{{ model.lender.city }} {{model.lender.state }} {{ model.lender.zip }}</p>
|
||||||
|
<p align='right'>Statement Date: {{ model.header.date }}</p>
|
||||||
|
|
||||||
|
<p/>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th width='45%' align='left'>Loan Information</th><th width='45%'> </th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>Borrower: {{ model.borrower.name }} </td> <td>Account Number: {{ model.parameters.account_number }}</td></tr>
|
||||||
|
<tr><td>{{ model.borrower.address }} </td> <td>Origination Date: {{ model.parameters.start_date }}</td></tr>
|
||||||
|
<tr><td>{{ model.borrower.city }}, {{model.borrower.state }} {{ model.borrower.zip }}</td>
|
||||||
|
<td>Original Principal: {{ "$%.2f"|format(model.parameters.principal) }}</td></tr>
|
||||||
|
<tr><td>Rate: {{model.parameters.interest_rate }}% </td> <td>Term: {{model.parameters.periods }} months </td></tr>
|
||||||
|
<tr><td>Next Payment Due Date: {{model.parameters.next_due_date}} </td> <td>Payment Due: {{ "$%.2f"|format(model.parameters.next_payment_amt) }} </td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p/>
|
||||||
|
<p class='section_header' color='red'><font face='arial' size='14'>Payment History</font></p>
|
||||||
|
<table>
|
||||||
|
<thead><tr>
|
||||||
|
<th width='5%'>#</td>
|
||||||
|
<th width='10%'>Due Date</th>
|
||||||
|
<th width='10%'>Date Paid</th>
|
||||||
|
<th width='10%'>Days Interest</th>
|
||||||
|
<th width='15%' align='right'>Payment Amt</th>
|
||||||
|
<th width='15%' align='right'>Principal Pmt</th>
|
||||||
|
<th width='15%' align='right'>Interest Pmt</th>
|
||||||
|
<th width='20%' align='right'>New Balance</th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in model.past_payments %}
|
||||||
|
<tr><td align='center'> {{ item.payment_number }} </td>
|
||||||
|
<td align='center'> {{ item.bill_date }} </td>
|
||||||
|
<td align='center'> {{ item.payment_date }} </td>
|
||||||
|
<td align='center'> {{ item.days_of_interest }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.payment_amount) }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.principal_payment) }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.interest_payment) }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.new_balance) }} </td>
|
||||||
|
</tr>
|
||||||
|
{% if item.month == 12 or loop.last %}
|
||||||
|
<tr><td colspan='8'"> Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.</td></tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<tr><td colspan='8'"> Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p/> <p/>
|
||||||
|
|
||||||
|
<p class='section_header'><font face='arial' size='14'>Remaining Amortization</font></p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width='8%'>#</th>
|
||||||
|
<th width='15%'>Due Date</th>
|
||||||
|
<th width='8%'>Days Interest</th>
|
||||||
|
<th width='15%' align='right'>Payment Amt</th>
|
||||||
|
<th width='15%' align='right'>Principal Pmt</th>
|
||||||
|
<th width='15%' align='right'>Interest Pmt</th>
|
||||||
|
<th width='20%' align='right'>Principal Balance</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in model.future_payments %}
|
||||||
|
<tr><td align='center'> {{ item.payment_number }} </td>
|
||||||
|
<td align='center'> {{ item.payment_date }} </td>
|
||||||
|
<td align='center'> {{ item.days_of_interest }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.payment_amount) }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.principal_payment) }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.interest_payment) }} </td>
|
||||||
|
<td align='right'> {{ "$%.2f"|format(item.new_balance) }} </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>Balloon Payment Due: {{ "$%.2f"|format(model.balloon_payment) }} </p>
|
||||||
|
</font>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -54,7 +54,8 @@
|
|||||||
|
|
||||||
<p class='section_header'><font face='arial' size='14'>Remaining Amortization</font></p>
|
<p class='section_header'><font face='arial' size='14'>Remaining Amortization</font></p>
|
||||||
<table>
|
<table>
|
||||||
<thead><tr>
|
<thead>
|
||||||
|
<tr>
|
||||||
<th width='8%'>#</th>
|
<th width='8%'>#</th>
|
||||||
<th width='15%'>Due Date</th>
|
<th width='15%'>Due Date</th>
|
||||||
<th width='8%'>Days Interest</th>
|
<th width='8%'>Days Interest</th>
|
||||||
@@ -62,7 +63,8 @@
|
|||||||
<th width='15%' align='right'>Principal Pmt</th>
|
<th width='15%' align='right'>Principal Pmt</th>
|
||||||
<th width='15%' align='right'>Interest Pmt</th>
|
<th width='15%' align='right'>Interest Pmt</th>
|
||||||
<th width='20%' align='right'>Principal Balance</th>
|
<th width='20%' align='right'>Principal Balance</th>
|
||||||
</tr></thead>
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in model.future_payments %}
|
{% for item in model.future_payments %}
|
||||||
<tr><td align='center'> {{ item.payment_number }} </td>
|
<tr><td align='center'> {{ item.payment_number }} </td>
|
||||||
134
web.py
134
web.py
@@ -2,9 +2,13 @@ from flask import Flask, render_template, request, redirect, url_for
|
|||||||
import json
|
import json
|
||||||
from decimal import *
|
from decimal import *
|
||||||
from datetime import *
|
from datetime import *
|
||||||
from jinja2 import Environment, FileSystemLoader
|
import jinja2
|
||||||
from fpdf import FPDF, HTMLMixin
|
from fpdf import FPDF, HTMLMixin
|
||||||
import smtplib
|
import smtplib
|
||||||
|
import os
|
||||||
|
|
||||||
|
loader=jinja2.FileSystemLoader([os.path.join(os.path.dirname(__file__),"templates")])
|
||||||
|
environment = jinja2.Environment(loader=loader)
|
||||||
|
|
||||||
from email.MIMEMultipart import MIMEMultipart
|
from email.MIMEMultipart import MIMEMultipart
|
||||||
from email.MIMEText import MIMEText
|
from email.MIMEText import MIMEText
|
||||||
@@ -21,6 +25,7 @@ def hello():
|
|||||||
loans.append( addLoan("2 Bear Houses LLC Loan", "9Kloan.json" ) )
|
loans.append( addLoan("2 Bear Houses LLC Loan", "9Kloan.json" ) )
|
||||||
loans.append( addLoan("3 Brenda Mortgage", "brendamortgage.json" ) )
|
loans.append( addLoan("3 Brenda Mortgage", "brendamortgage.json" ) )
|
||||||
loans.append( addLoan("4 Dad Mortgage", "dadmortgage.json") )
|
loans.append( addLoan("4 Dad Mortgage", "dadmortgage.json") )
|
||||||
|
loans.append( addLoan("5 Test Loan", "testloan.json"))
|
||||||
|
|
||||||
if 'loan' in request.args:
|
if 'loan' in request.args:
|
||||||
filename = request.args["loan"]
|
filename = request.args["loan"]
|
||||||
@@ -34,12 +39,49 @@ def hello():
|
|||||||
|
|
||||||
@app.route('/update_file')
|
@app.route('/update_file')
|
||||||
def update_file():
|
def update_file():
|
||||||
return
|
loanFile = request.form['loan']
|
||||||
|
return redirect( '/?loan=' + loan )
|
||||||
|
|
||||||
@app.route('/send_statement', methods=['POST'])
|
@app.route('/send_statement', methods=['POST'])
|
||||||
def send_statement():
|
def send_statement():
|
||||||
loan = request.form["loan"]
|
loanFile = request.form["loan"]
|
||||||
redirect( '/?loan=' + loan )
|
subject = request.form["subject"]
|
||||||
|
message = request.form["message"]
|
||||||
|
style = request.form["style"]
|
||||||
|
|
||||||
|
loan = loadLoanInformation('/Users/john/PycharmProjects/mortgage/' + loanFile)
|
||||||
|
amortizeLoan(loan)
|
||||||
|
|
||||||
|
reportCreated = False
|
||||||
|
textReport = pdfReport = htmlReport = None
|
||||||
|
|
||||||
|
if 'text' in request.form:
|
||||||
|
textReport = transformTemplate(selectTemplate('text'), loan)
|
||||||
|
reportCreated = True
|
||||||
|
|
||||||
|
if 'pdf' in request.form:
|
||||||
|
pdfInterimReport = transformTemplate(selectTemplate('pdf'), loan)
|
||||||
|
pdfReport = createPDF(pdfInterimReport)
|
||||||
|
if style == 'embed':
|
||||||
|
style = 'attach'
|
||||||
|
reportCreated = True
|
||||||
|
|
||||||
|
if ('html' in request.form) or (reportCreated is False):
|
||||||
|
htmlReport = transformTemplate(selectTemplate('html'), loan)
|
||||||
|
|
||||||
|
|
||||||
|
# send email
|
||||||
|
emailParameters = loan["email"]
|
||||||
|
|
||||||
|
msg = generateEmail( emailParameters["from_address"],
|
||||||
|
emailParameters["to_address"],
|
||||||
|
subject,
|
||||||
|
message, pdfReport, htmlReport, textReport)
|
||||||
|
|
||||||
|
sendEmail(msg, emailParameters["from_address"], emailParameters["to_address"], emailParameters['password'])
|
||||||
|
|
||||||
|
|
||||||
|
return render_template('email.html', filename=loanFile)
|
||||||
|
|
||||||
def addLoan(loanName, fileName):
|
def addLoan(loanName, fileName):
|
||||||
x = {}
|
x = {}
|
||||||
@@ -47,7 +89,10 @@ def addLoan(loanName, fileName):
|
|||||||
x['filename'] = fileName
|
x['filename'] = fileName
|
||||||
return x
|
return x
|
||||||
|
|
||||||
'''from old code'''
|
###################
|
||||||
|
# from old code #
|
||||||
|
###################
|
||||||
|
|
||||||
def getStatementHeader(datastore):
|
def getStatementHeader(datastore):
|
||||||
return datastore['header']
|
return datastore['header']
|
||||||
|
|
||||||
@@ -238,6 +283,85 @@ def amortizeLoan(loan):
|
|||||||
loan["future_payments"] = future_payments
|
loan["future_payments"] = future_payments
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def transformTemplate(template_fileName, loanModel):
|
||||||
|
# template_filename = "statement.text.jinja"
|
||||||
|
# setup jinja for creating the statement
|
||||||
|
template = environment.get_template(template_fileName)
|
||||||
|
|
||||||
|
print loanModel
|
||||||
|
report = template.render(model=loanModel)
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
def generateEmail(from_address, to_address, subject, body, pdf, html, txt):
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg['Subject'] = subject
|
||||||
|
msg['From'] = from_address
|
||||||
|
msg['To'] = to_address
|
||||||
|
|
||||||
|
msg.attach(MIMEText(body))
|
||||||
|
|
||||||
|
if (pdf != None):
|
||||||
|
part = MIMEBase("application", "octet-stream")
|
||||||
|
part.set_payload(pdf)
|
||||||
|
Encoders.encode_base64(part)
|
||||||
|
part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"')
|
||||||
|
msg.attach(part)
|
||||||
|
|
||||||
|
if (html != None):
|
||||||
|
part = MIMEBase("text", "html")
|
||||||
|
part.set_payload(html)
|
||||||
|
Encoders.encode_base64(part)
|
||||||
|
part.add_header('Content-Disposition', 'attachment; filename="statement.html"')
|
||||||
|
msg.attach(part)
|
||||||
|
|
||||||
|
if (txt != None):
|
||||||
|
part = MIMEBase("text", "plain")
|
||||||
|
part.set_payload(txt)
|
||||||
|
Encoders.encode_base64(part)
|
||||||
|
part.add_header('Content-Disposition', 'attachment; filename="statement.txt"')
|
||||||
|
msg.attach(part)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def sendEmail(msg, from_address, to_address, passwd):
|
||||||
|
try:
|
||||||
|
server = smtplib.SMTP('smtp.gmail.com', 587)
|
||||||
|
server.ehlo()
|
||||||
|
server.starttls()
|
||||||
|
server.login(from_address, passwd)
|
||||||
|
|
||||||
|
server.sendmail(from_address, to_address, msg.as_string())
|
||||||
|
server.close()
|
||||||
|
except:
|
||||||
|
print "Couldn't send email."
|
||||||
|
|
||||||
|
|
||||||
|
def createPDF(report):
|
||||||
|
# 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')
|
||||||
|
return pdf.output(dest='S')
|
||||||
|
|
||||||
|
|
||||||
|
def selectTemplate(format):
|
||||||
|
if format == 'html':
|
||||||
|
return 'statement.html.jinja'
|
||||||
|
|
||||||
|
if format == 'pdf':
|
||||||
|
return 'statement.pdf.jinja'
|
||||||
|
|
||||||
|
if format == 'text':
|
||||||
|
return 'statement.text.jinja'
|
||||||
|
|
||||||
|
return 'statement.html.jinja'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.debug = True
|
app.debug = True
|
||||||
|
|||||||
Reference in New Issue
Block a user