From 53327a9e092a7b40350e04408431098752450154 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Wed, 19 Dec 2018 23:09:04 -0500 Subject: [PATCH 01/38] Initial commit. Things dont work yet. -- user: JohnKent branch 'default' added greenfield_mortgage.txt added mortgage.py added mortgage_template.py added statement.pdf.jinja added statement.txt.jinja --- greenfield_mortgage.txt | 25 ++++ mortgage.py | 130 ++++++++++++++++++ mortgage_template.py | 290 ++++++++++++++++++++++++++++++++++++++++ statement.pdf.jinja | 82 ++++++++++++ statement.txt.jinja | 49 +++++++ 5 files changed, 576 insertions(+) create mode 100644 greenfield_mortgage.txt create mode 100644 mortgage.py create mode 100644 mortgage_template.py create mode 100644 statement.pdf.jinja create mode 100644 statement.txt.jinja diff --git a/greenfield_mortgage.txt b/greenfield_mortgage.txt new file mode 100644 index 0000000..91bc430 --- /dev/null +++ b/greenfield_mortgage.txt @@ -0,0 +1,25 @@ +{ +"principal": 97750.00, +"interest rate": 5.5, +"periods per year": 12, +"periods": 182, +"start date": "2017-11-07", +"start_interest_date": "2017-11-11", +"first payment month": "2017-12-15", +"monthly_payment": 803.00, +"payment day of month": "15", +"payments": [ + ["2017-12-11", "803.00"], + ["2018-01-23", "803.00"], + ["2018-03-23", "803.00"], + ["2018-04-18", "803.00"], + ["2018-04-26", "803.00"], + ["2018-05-15", "0.00"], + ["2018-06-15", "0.00"], + ["2018-07-12", "803.00"], + ["2018-08-07", "803.00"], + ["2018-09-06", "803.00"], + ["2018-10-11", "803.00"], + ["2018-11-13", "803.00"] + ] +} diff --git a/mortgage.py b/mortgage.py new file mode 100644 index 0000000..18a582e --- /dev/null +++ b/mortgage.py @@ -0,0 +1,130 @@ +import json +from decimal import * +from datetime import * + +# 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. + +#read in the file +#filename = "./10Kloan.txt" +filename = "./dadmortgage.txt" +#filename = "./brendamortgage.txt" +#filename = "./greenfield_mortgage.txt" + +if filename: + with open(filename, 'r') as f: + datastore = json.load(f) + +#read in the loan profile information +annual_rate = Decimal(datastore['interest rate'])/100 +daily_interest_rate = annual_rate/360 +principal = Decimal(datastore["principal"]).quantize(Decimal("1.00")) +periods_per_year = Decimal(datastore["periods per year"]) +total_periods = Decimal(datastore["periods"]) +payment_day_of_month = int(datastore['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")) + + + +print "Principal: ", principal +print "Interest Rate: ", annual_rate +print "Payments Per Year: ", periods_per_year +print "Loan Term: ", total_periods +print "Monthly Payment", monthly_payment +print "Payments due day: ", payment_day_of_month + +print + +#loop over the payments and calculate the actual amortization +actual_payments = datastore["payments"] + +remaining_principal = principal +annual_interest = 0 +total_interest = 0 + +interest_paid_through_date = datetime.strptime(datastore["start_interest_date"], "%Y-%m-%d").date() +next_payment_date = datetime.strptime(datastore["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 + +print "Payment History:" +print "\tBill\t\t\tPayment\tDays of\tPayment\t\tRemaining\t\tPrincipal\tInterest\t\t\tNew" +print "#\tDate\t\t\tDate\tInterst\tAmount\t\tPrincipal\t\tPayment\t\tPayment\t\t\tBalance" +for payment in actual_payments: + payment_date = datetime.strptime((payment[0]), '%Y-%m-%d').date() + payment_amount = Decimal(payment[1]).quantize(Decimal("1.00")) + #print next_bill_date, "\t", next_payment_date, "\t", + days_since_last_payment = (payment_date-interest_paid_through_date).days + #print days_since_last_payment + new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) +# new_principal = monthly_payment - new_interest + new_principal = payment_amount - new_interest + + print payment_number, " ", next_bill_date, "\t\t", payment_date, "\t", days_since_last_payment, "\t", payment_amount, "\t\t\t", remaining_principal, "\t\t", new_principal,\ + "\t\t", 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 + print "\t\t\t", remaining_principal + 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) + print "Total interest paid to date for", old_bill_date.year, " is", annual_interest + else: + next_bill_date = date(year=old_bill_date.year+1, month = 1, day=payment_day_of_month) + print "Total interest for ", old_bill_date.year, " was ", annual_interest + annual_interest = 0 + +print "Total interest paid to date on this loan is", total_interest + +#loop over remaining scheduled payments and present estimated amortization +print "\nEstimated future amortization" + +while (payment_number <= total_periods) and (remaining_principal > 0): + print payment_number, "\t", + payment_number = payment_number+1 + + print next_bill_date, + + days_since_last_payment = (next_bill_date-interest_paid_through_date).days + print days_since_last_payment, "\t", + 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 + + print monthly_payment, "\t\t\t", remaining_principal, "\t\t", new_principal,\ + "\t\t", new_interest, + + remaining_principal = remaining_principal - new_principal + print "\t", remaining_principal + + interest_paid_through_date = next_bill_date + 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) + +if remaining_principal > 0: + print 'Balloon payment due:', remaining_principal diff --git a/mortgage_template.py b/mortgage_template.py new file mode 100644 index 0000000..29ea278 --- /dev/null +++ b/mortgage_template.py @@ -0,0 +1,290 @@ +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 loadLoanInformation(filename): + + datastore = getDataStore(filename) + + loanModel['loan'] = getLoan(dataStore) + loanModel['lender'] = getLender(datastore) + loanModel['borrower'] = getBorrower(datastore) + loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate) + loanModel['futureAmortization'] = getAmortization(datastore, loan, paymentHistory) + return loanModel + +def getLoan(datastore): + # read in the loan profile information + + 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): + #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): + #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=principal, future_payments=future_payments, \ + past_payments=past_payments, balloon_payment=remaining_principal, \ + total_interest_paid_to_date=total_interest, statement=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 = loadModel.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 = principal + 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) + else: + annual_interest_record['year'] = old_bill_date.year + annual_interest_record['interest'] = annual_interest + past_interest.append(annual_interest_record) + next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month) + payment_record['print_interest_total'] = True + payment_record[ + 'interest_total_message'] = "Total interest for " + old_bill_date.year.__str__() + " was $" + annual_interest.__str__() + annual_interest = 0 + + if old_bill_date.month < 12: + payment_record['print_interest_total'] = True + payment_record[ + 'interest_total_message'] = "Total interest to date for " + old_bill_date.year.__str__() + " is $" + annual_interest.__str__() + + # 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() diff --git a/statement.pdf.jinja b/statement.pdf.jinja new file mode 100644 index 0000000..b1a43cd --- /dev/null +++ b/statement.pdf.jinja @@ -0,0 +1,82 @@ + + +

{{ statement.title }}

+

{{ statement.lender.name }}

+ +

{{ statement.lender.phone }} - {{ statement.lender.address }} - +{{ statement.lender.city }} {{statement.lender.state }} {{ statement.lender.zip }}

+

Statement Date: {{ statement.date }}

+ +

+ + + + + + + + + + +
Loan Information 
Borrower: {{ statement.borrower.name }}  Account Number: {{ statement.loan.account_number }}
{{ statement.borrower.address }}  Origination Date: {{ statement.loan.origination_date }}
{{ statement.borrower.city }}, {{statement.borrower.state }} {{ statement.borrower.zip }}Original Principal: {{ "$%.2f"|format(statement.loan.principal) }}
Rate: {{statement.loan.rate }} Term: {{statement.loan.term }} months
Next Payment Due Date: {{statement.loan.next_due_date}} Payment Due: {{ "$%.2f"|format(statement.loan.next_payment_amt) }}
+

+

Payment History

+ + + + + + + + + + + +{% for item in past_payments %} + + + + + + + + + + {% if item.print_interest_total %} + + {% endif %} +{% endfor %} + +
# + Due DateDate PaidDays InterestPayment AmtPrincipal PmtInterest PmtNew Balance
{{ 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) }}
{{ item.interest_total_message }}
+

Total interest paid to date is {{ "$%.2f"|format(total_interest_paid_to_date) }}.

+

+ +

Remaining Amortization

+ + + + + + + + + + + +{% for item in future_payments %} + + + + + + + + +{% endfor %} + +
#Due DateDays InterestPayment AmtPrincipal PmtInterest PmtPrincipal Balance
{{ 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) }}
+

Balloon Payment Due: {{ "$%.2f"|format(balloon_payment) }}

+
+ + \ No newline at end of file diff --git a/statement.txt.jinja b/statement.txt.jinja new file mode 100644 index 0000000..d7fbef2 --- /dev/null +++ b/statement.txt.jinja @@ -0,0 +1,49 @@ +Original Principal Balance: {{ original_principal_balance }} + +Payment History + #\tBill Date + Payment Date + Days of Interest + Payment Amount + Principal Payment + Interest Payment + New Balance + +{% for item in past_payments %} +{{ item.payment_number }} +{{ item.bill_date }} +{{ item.payment_date }} +{{ item.days_of_interest }} +{{ item.payment_amount }} +{{ item.principal_payment }} +{{ item.interest_payment }} +{{ item.new_balance }} + + {% if item.print_interest_total %} + {{ item.interest_total_message }} + {% endif %} +{% endfor %} +Total interest paid to date is {{ total_interest_paid_to_date }}. + + +Remaining Amortization + # + Days of Interest + Due Date + Payment Amount + Scheduled Principal Payment + Scheduled Interest Payment + Expected New Balance + +{% for item in future_payments %} +{{ item.payment_number }} +{{ item.days_of_interest }} +{{ item.payment_date }} +{{ item.payment_amount }} +{{ item.principal_payment }} +{{ item.interest_payment }} +{{ item.new_balance }} +{% endfor %} + +Balloon Payment Due: {{ balloon_payment }} + From e274b96672598a0e35084598cbd74fcb9f89eb53 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 23 Dec 2018 01:05:36 -0500 Subject: [PATCH 02/38] Fixing references. --- mortgage_template.py | 67 +++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/mortgage_template.py b/mortgage_template.py index 29ea278..38e67a9 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -9,19 +9,30 @@ 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 = {} - datastore = getDataStore(filename) - - loanModel['loan'] = getLoan(dataStore) + loanModel['loan'] = getLoan(datastore) loanModel['lender'] = getLender(datastore) loanModel['borrower'] = getBorrower(datastore) loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate) - loanModel['futureAmortization'] = getAmortization(datastore, loan, paymentHistory) + 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 @@ -49,8 +60,10 @@ def getLoan(datastore): loan['next_payment_date'] = '12/12/12' return loan + def getLender(datastore): - #to be replaced with real loading code + 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' @@ -59,8 +72,10 @@ def getLender(datastore): lender['zip'] = '20011' return getLender + def getBorrower(datastore): - #to be replaced with real loading code + borrower = {} + # to be replaced with real loading code borrower['name'] = 'Bear Houses, LLC' borrower['address'] = '123 Any Street' borrower['city'] = 'Alltown' @@ -68,7 +83,8 @@ def getBorrower(datastore): borrower['zip'] = '11111' return borrower -def getDataStore(filename=None): + +def getDatastore(filename=None): try: if filename: with open(filename, 'r') as f: @@ -84,31 +100,34 @@ def getDataStore(filename=None): 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=principal, future_payments=future_payments, \ - past_payments=past_payments, balloon_payment=remaining_principal, \ - total_interest_paid_to_date=total_interest, statement=statement) + 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 @@ -126,6 +145,7 @@ def generateEmail(from_address, to_address, subject, body, attachment): 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 @@ -156,7 +176,7 @@ def main(): statement, lender, borrower, loan = {}, {}, {}, {} lender = loanModel.lender borrower = loanModel.borrower - past_payments = loadModel.past_payments + past_payments = loanModel.past_payments future_payments = loanModel.future_payments statement['title'] = "Mortgage Statement - 185 James River Rd" @@ -168,7 +188,10 @@ def main(): # loop over the payments and calculate the actual amortization actual_payments = loanModel["payments"] - remaining_principal = principal + 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 @@ -209,20 +232,6 @@ def main(): 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_record['year'] = old_bill_date.year - annual_interest_record['interest'] = annual_interest - past_interest.append(annual_interest_record) - next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month) - payment_record['print_interest_total'] = True - payment_record[ - 'interest_total_message'] = "Total interest for " + old_bill_date.year.__str__() + " was $" + annual_interest.__str__() - annual_interest = 0 - - if old_bill_date.month < 12: - payment_record['print_interest_total'] = True - payment_record[ - 'interest_total_message'] = "Total interest to date for " + old_bill_date.year.__str__() + " is $" + annual_interest.__str__() # loop over remaining scheduled payments and present estimated amortization while (payment_number <= total_periods) and (remaining_principal > 0): @@ -257,7 +266,7 @@ def main(): else: next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month) - report = transformTemplate('',loanModel) + report = transformTemplate('', loanModel) # create pdf class MyFPDF(FPDF, HTMLMixin): From 45d6e32149ab613310ea1b3d8e6b5f53f8765eb0 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 6 Jan 2019 16:39:54 -0500 Subject: [PATCH 03/38] Updated all of the payment files to reflect new format. Updated the PDF generation template. Updated the email sending code. Fixed a few off by one errors when the last payment also is the last payment of the year. --- 10Kloan.json | 63 ++++++++ brendamortgage.json | 56 +++++++ dadmortgage.json | 56 +++++++ greenfield_mortgage.json | 63 ++++++++ greenfield_mortgage.txt | 25 ---- mortgage_template.py | 307 ++++++++++++++++++++------------------- statement.pdf.jinja | 30 ++-- 7 files changed, 409 insertions(+), 191 deletions(-) create mode 100644 10Kloan.json create mode 100644 brendamortgage.json create mode 100644 dadmortgage.json create mode 100644 greenfield_mortgage.json delete mode 100644 greenfield_mortgage.txt diff --git a/10Kloan.json b/10Kloan.json new file mode 100644 index 0000000..b9cd72d --- /dev/null +++ b/10Kloan.json @@ -0,0 +1,63 @@ +{ +"htmlTemplate": "statement.pdf.jinja", +"txtTemplate": "statement.txt.jinja", +"format": "html", +"email": { + "from_address": "jkent3rd@gmail.com", + "to_address": "grady@gradystreet.com", + "server": "smtp.gmail.com", + "password": "pvyrbcnzrjoizprn", + "template": "./template.txt", + "send_pdf": "true", + "send_text": "true", + "subject": "Your loan statement...", + "body": "Your loan statement is attached.", + "text": "Please see your most recent account statement." + }, +"parameters": { + "account_number": "100100", + "principal": 10020.00, + "interest_rate": 6.5, + "periods_per_year": 12, + "periods": 18, + "start_date": "2017-12-05", + "start_interest_date": "2017-12-05", + "first_payment_month": "2018-01-15", + "monthly_payment": 589.00, + "payment_day_of_month": "15" + }, +"payments": [ + ["2018-01-16", "589.00"], + ["2018-03-30", "589.00"], + ["2018-04-06", "589.00"], + ["2018-04-12", "589.00"], + ["2018-05-28", "589.00"], + ["2018-06-12", "589.00"], + ["2018-07-13", "589.00"], + ["2018-08-14", "589.00"], + ["2018-09-26", "589.00"], + ["2018-10-15", "0"], + ["2018-11-29", "589.00"], + ["2018-12-31", "589.00"] + ], +"borrower": { + "name": "Bear Houses, LLC", + "address": "301 N Beauregard St Apt 203", + "city": "Alexandria", + "state": "VA", + "zip": "22312" + }, +"lender": { + "name": "John Kent", + "phone": "703.343.0782", + "address": "743 Madison St NW", + "city": "Washington", + "state": "DC", + "zip": "20011" + }, +"header": { + "title": "Installment Loan Statement", + "date": "Today" + } +} + diff --git a/brendamortgage.json b/brendamortgage.json new file mode 100644 index 0000000..d13476f --- /dev/null +++ b/brendamortgage.json @@ -0,0 +1,56 @@ +{ +"htmlTemplate": "statement.pdf.jinja", +"txtTemplate": "statement.txt.jinja", +"format": "html", +"email": { + "from_address": "jkent3rd@gmail.com", + "to_address": "bck@virginia.edu", + "server": "smtp.gmail.com", + "password": "pvyrbcnzrjoizprn", + "template": "./template.txt", + "send_pdf": "true", + "send_text": "true", + "subject": "Your loan statement...", + "body": "Your loan statement is attached.", + "text": "Please see your most recent account statement." + }, +"parameters": { + "account_number": "100001", + "principal": 35000.00, + "interest_rate": 5.0, + "periods_per_year": 12, + "periods": 180, + "start_date": "2018-06-25", + "start_interest_date": "2018-07-01", + "first_payment_month": "2018-08-01", + "monthly_payment": 278.15, + "payment_day_of_month": "01" + }, +"borrower": { + "name": "Grandma Tina's Properties, LLC", + "address": "743 Madison St NW", + "city": "Washington", + "state": "DC", + "zip": "20011" + }, +"lender": { + "name": "John Kent", + "phone": "434-510-7272", + "address": "109 Shores Rd", + "city": "Palmyra", + "state": "VA", + "zip": "22963" + }, +"header": { + "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA", + "date": "Today" + }, +"payments": [ + ["2018-08-01", "278.15"], + ["2018-09-01", "278.15"], + ["2018-10-01", "278.15"], + ["2018-11-01", "278.15"], + ["2018-12-01", "278.15"], + ["2019-01-01", "278.15"] + ] +} diff --git a/dadmortgage.json b/dadmortgage.json new file mode 100644 index 0000000..9ff0acb --- /dev/null +++ b/dadmortgage.json @@ -0,0 +1,56 @@ +{ +"htmlTemplate": "statement.pdf.jinja", +"txtTemplate": "statement.txt.jinja", +"format": "html", +"email": { + "from_address": "jkent3rd@gmail.com", + "to_address": "johnkent49@gmail.com", + "server": "smtp.gmail.com", + "password": "pvyrbcnzrjoizprn", + "template": "./template.txt", + "send_pdf": "true", + "send_text": "true", + "subject": "Your loan statement...", + "body": "Your loan statement is attached.", + "text": "Please see your most recent account statement." + }, +"parameters": { + "account_number": "100001", + "principal": 35000.00, + "interest_rate": 5.0, + "periods_per_year": 12, + "periods": 180, + "start_date": "2018-06-25", + "start_interest_date": "2018-07-01", + "first_payment_month": "2018-08-01", + "monthly_payment": 278.15, + "payment_day_of_month": "01" + }, +"borrower": { + "name": "Grandma Tina's Properties, LLC", + "address": "743 Madison St NW", + "city": "Washington", + "state": "DC", + "zip": "20011" + }, +"lender": { + "name": "John Kent", + "phone": "434-510-7272", + "address": "109 Shores Rd", + "city": "Palmyra", + "state": "VA", + "zip": "22963" + }, +"header": { + "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA", + "date": "Today" + }, +"payments": [ + ["2018-08-01", "278.15"], + ["2018-09-01", "278.15"], + ["2018-10-01", "278.15"], + ["2018-11-01", "278.15"], + ["2018-12-01", "278.15"], + ["2019-01-01", "278.15"] + ] +} diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json new file mode 100644 index 0000000..65d2127 --- /dev/null +++ b/greenfield_mortgage.json @@ -0,0 +1,63 @@ +{ +"htmlTemplate": "statement.pdf.jinja", +"txtTemplate": "statement.txt.jinja", +"format": "html", +"email": { + "from_address": "jkent3rd@gmail.com", + "to_address": "grady@gradystreet.com", + "server": "smtp.gmail.com", + "password": "pvyrbcnzrjoizprn", + "template": "./template.txt", + "send_pdf": "true", + "send_text": "true", + "subject": "Your loan statement...", + "body": "Your loan statement is attached.", + "text": "Please see your most recent account statement." + }, +"parameters": { + "account_number": "100001", + "principal": 97750.00, + "interest_rate": 5.5, + "periods_per_year": 12, + "periods": 182, + "start_date": "2017-11-07", + "start_interest_date": "2017-11-11", + "first_payment_month": "2017-12-15", + "monthly_payment": 803.00, + "payment_day_of_month": "15" + }, +"payments": [ + ["2017-12-11", "803.00"], + ["2018-01-23", "803.00"], + ["2018-03-23", "803.00"], + ["2018-04-18", "803.00"], + ["2018-04-26", "803.00"], + ["2018-05-15", "0.00"], + ["2018-06-15", "0.00"], + ["2018-07-12", "803.00"], + ["2018-08-07", "803.00"], + ["2018-09-06", "803.00"], + ["2018-10-11", "803.00"], + ["2018-11-13", "803.00"], + ["2018-12-13", "803.00"] + ], +"borrower": { + "name": "Bear Houses, LLC", + "address": "301 N Beauregard St Apt 203", + "city": "Alexandria", + "state": "VA", + "zip": "22312" + }, +"lender": { + "name": "Rivanna Graphite Investments, LLC", + "phone": "703.343.0782", + "address": "743 Madison St NW", + "city": "Washington", + "state": "DC", + "zip": "20011" + }, +"header": { + "title": "Mortgage Loan Statement - 195 Greenfield Lane, Pearl MS", + "date": "Today" + } +} diff --git a/greenfield_mortgage.txt b/greenfield_mortgage.txt deleted file mode 100644 index 91bc430..0000000 --- a/greenfield_mortgage.txt +++ /dev/null @@ -1,25 +0,0 @@ -{ -"principal": 97750.00, -"interest rate": 5.5, -"periods per year": 12, -"periods": 182, -"start date": "2017-11-07", -"start_interest_date": "2017-11-11", -"first payment month": "2017-12-15", -"monthly_payment": 803.00, -"payment day of month": "15", -"payments": [ - ["2017-12-11", "803.00"], - ["2018-01-23", "803.00"], - ["2018-03-23", "803.00"], - ["2018-04-18", "803.00"], - ["2018-04-26", "803.00"], - ["2018-05-15", "0.00"], - ["2018-06-15", "0.00"], - ["2018-07-12", "803.00"], - ["2018-08-07", "803.00"], - ["2018-09-06", "803.00"], - ["2018-10-11", "803.00"], - ["2018-11-13", "803.00"] - ] -} diff --git a/mortgage_template.py b/mortgage_template.py index 38e67a9..df1cb4b 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -4,45 +4,47 @@ 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 getStatementHeader(datastore): + return datastore['header'] -def getAmortization(datastore, loan, paymentHistory): - pass +def getEmailInformation(datastore): + return datastore['email'] def loadLoanInformation(filename): datastore = getDatastore(filename) - loanModel = {} - loanModel['loan'] = getLoan(datastore) + loanModel = {} + loanModel['datastore'] = datastore + loanModel['email'] = getEmailInformation(datastore) + loanModel['parameters'] = getLoanParameters(datastore) loanModel['lender'] = getLender(datastore) loanModel['borrower'] = getBorrower(datastore) - loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate) - loanModel['futureAmortization'] = getAmortization(datastore, loan, loanModel.paymentHistory) + loanModel['header'] = getStatementHeader(datastore) return loanModel -def getLoan(datastore): +def getLoanParameters(datastore): # read in the loan profile information - loan = {} + loan = datastore['parameters'] - annual_rate = Decimal(datastore['loan.interest rate']) / 100 + annual_rate = Decimal(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']) + 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 datastore: - monthly_payment = Decimal(datastore["monthly_payment"]).quantize(Decimal("1.00")) + 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 @@ -50,38 +52,24 @@ def getLoan(datastore): 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['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): - 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 + return datastore['lender'] 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 + return datastore['borrower'] def getDatastore(filename=None): @@ -91,7 +79,7 @@ def getDatastore(filename=None): datastore = json.load(f) except Exception as e: - print "An error occurred opening your loan file '#s'. " % filename + print "An error occurred opening your loan file '%s'. " % filename print "The Exception:" print e.__repr__() quit() @@ -99,109 +87,29 @@ def getDatastore(filename=None): 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 - +def amortizeLoan(loan): # loop over the payments and calculate the actual amortization - actual_payments = loanModel["payments"] + monthly_payment = loan["parameters"]["monthly_payment"] - 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 + 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 - - 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 + 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")) @@ -232,6 +140,16 @@ def main(): 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["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): @@ -254,7 +172,7 @@ def main(): 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['principal_payment'] = new_principal future_payment_record['interest_payment'] = new_interest future_payment_record['new_balance'] = remaining_principal future_payments.append(future_payment_record) @@ -266,8 +184,68 @@ def main(): else: next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month) - report = transformTemplate('', loanModel) + loan["balloon_payment"] = remaining_principal + loan["past_payments"] = past_payments + loan["future_payments"] = future_payments + return + + +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) + + print loanModel + report = template.render(model=loanModel) + return report + + +def generatePDFAttachment(): + template_filename = "statement.pdf.jinja" + pass + + +def generateEmail(from_address, to_address, subject, body, pdfAttachment, txtAttachment): + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = from_address + msg['To'] = to_address + + msg.attach(MIMEText(body)) + + if (pdfAttachment != None): + part = MIMEBase("application", "octet-stream") + part.set_payload(pdfAttachment) + Encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"') + msg.attach(part) + + if (txtAttachment != None): + part = MIMEBase("text", "html") + part.set_payload(txtAttachment) + Encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="statement.html"') + 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 @@ -277,22 +255,49 @@ def main(): pdf.add_page() pdf.write_html(report) # pdf.output(name='test.pdf', dest='F') - attachment = pdf.output(dest='S') + return pdf.output(dest='S') + + +def selectTemplate(loan): + templateKey = loan["datastore"]["format"] + "Template" + if templateKey in loan: + template = loan[templateKey] + else: + template = 'statement.pdf.jinja' + + return template + +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. + + # read in the file + # filename = "./10Kloan.txt" + #filename = "./10Kloan.json" + # filename = "./dadmortgage.json" + # filename = "./brendamortgage.json" + filename = "./greenfield_mortgage.json" + template_filename = "statement.pdf.jinja" + + loan = loadLoanInformation(filename) + + amortizeLoan(loan) + + report = transformTemplate(selectTemplate(loan), loan) + + if loan["email"]["send_pdf"] == "true": + pdfAttachment = createPDF(report) + # 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() + emailParameters = loan["email"] + msg = generateEmail(emailParameters["from_address"], emailParameters["to_address"], emailParameters["subject"], + emailParameters["body"], pdfAttachment, report) + sendEmail(msg, emailParameters["from_address"], emailParameters["to_address"], emailParameters['password']) if __name__ == '__main__': diff --git a/statement.pdf.jinja b/statement.pdf.jinja index b1a43cd..1e2370c 100644 --- a/statement.pdf.jinja +++ b/statement.pdf.jinja @@ -1,22 +1,22 @@ -

{{ statement.title }}

-

{{ statement.lender.name }}

+

{{ model.header.title }}

+

{{ model.lender.name }}

-

{{ statement.lender.phone }} - {{ statement.lender.address }} - -{{ statement.lender.city }} {{statement.lender.state }} {{ statement.lender.zip }}

-

Statement Date: {{ statement.date }}

+

{{ model.lender.phone }} - {{ model.lender.address }} - +{{ model.lender.city }} {{model.lender.state }} {{ model.lender.zip }}

+

Statement Date: {{ model.header.date }}

- - - - - - + + + + + +
Loan Information 
Borrower: {{ statement.borrower.name }}  Account Number: {{ statement.loan.account_number }}
{{ statement.borrower.address }}  Origination Date: {{ statement.loan.origination_date }}
{{ statement.borrower.city }}, {{statement.borrower.state }} {{ statement.borrower.zip }}Original Principal: {{ "$%.2f"|format(statement.loan.principal) }}
Rate: {{statement.loan.rate }} Term: {{statement.loan.term }} months
Next Payment Due Date: {{statement.loan.next_due_date}} Payment Due: {{ "$%.2f"|format(statement.loan.next_payment_amt) }}
Borrower: {{ model.borrower.name }}  Account Number: {{ model.parameters.account_number }}
{{ model.borrower.address }}  Origination Date: {{ model.parameters.start_date }}
{{ model.borrower.city }}, {{model.borrower.state }} {{ model.borrower.zip }}Original Principal: {{ "$%.2f"|format(model.parameters.principal) }}
Rate: {{model.parameters.interest_rate }}% Term: {{model.parameters.periods }} months
Next Payment Due Date: {{model.parameters.next_due_date}} Payment Due: {{ "$%.2f"|format(model.parameters.next_payment_amt) }}

@@ -33,7 +33,7 @@ New Balance -{% for item in past_payments %} +{% for item in model.past_payments %} {{ item.payment_number }} {{ item.bill_date }} {{ item.payment_date }} @@ -49,7 +49,7 @@ {% endfor %} -

Total interest paid to date is {{ "$%.2f"|format(total_interest_paid_to_date) }}.

+

Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.

Remaining Amortization

@@ -64,7 +64,7 @@ Principal Balance -{% for item in future_payments %} +{% for item in model.future_payments %} {{ item.payment_number }} {{ item.payment_date }} {{ item.days_of_interest }} @@ -76,7 +76,7 @@ {% endfor %} -

Balloon Payment Due: {{ "$%.2f"|format(balloon_payment) }}

+

Balloon Payment Due: {{ "$%.2f"|format(model.balloon_payment) }}

\ No newline at end of file From 7612f854d7d5f2575560955383ddc4f4ce3ddda3 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 13 Jan 2019 14:53:53 -0500 Subject: [PATCH 04/38] Fixed the program to print the annual interest paid. --- greenfield_mortgage.json | 3 ++- mortgage_template.py | 8 +++++--- statement.pdf.jinja | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json index 65d2127..c42064f 100644 --- a/greenfield_mortgage.json +++ b/greenfield_mortgage.json @@ -39,7 +39,8 @@ ["2018-09-06", "803.00"], ["2018-10-11", "803.00"], ["2018-11-13", "803.00"], - ["2018-12-13", "803.00"] + ["2018-12-13", "803.00"], + ["2018-01-11", "803.00"] ], "borrower": { "name": "Bear Houses, LLC", diff --git a/mortgage_template.py b/mortgage_template.py index df1cb4b..69459c2 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -123,6 +123,8 @@ def amortizeLoan(loan): # 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 @@ -276,11 +278,11 @@ def main(): # at closing. The first payment will incur interest from one month before the bill is due. # read in the file - # filename = "./10Kloan.txt" - #filename = "./10Kloan.json" + filename = "./testloan.json" + # filename = "./10Kloan.json" # filename = "./dadmortgage.json" # filename = "./brendamortgage.json" - filename = "./greenfield_mortgage.json" + #filename = "./greenfield_mortgage.json" template_filename = "statement.pdf.jinja" loan = loadLoanInformation(filename) diff --git a/statement.pdf.jinja b/statement.pdf.jinja index 1e2370c..288e119 100644 --- a/statement.pdf.jinja +++ b/statement.pdf.jinja @@ -43,13 +43,13 @@ {{ "$%.2f"|format(item.interest_payment) }} {{ "$%.2f"|format(item.new_balance) }} - {% if item.print_interest_total %} - {{ item.interest_total_message }} + {% 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) }}. -

Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.

Remaining Amortization

From f5c78ca7dc4742f25fb40427d0a09fef51211455 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 13 Jan 2019 14:54:30 -0500 Subject: [PATCH 05/38] Added this file for testing of email function. --- testloan.json | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 testloan.json diff --git a/testloan.json b/testloan.json new file mode 100644 index 0000000..f0cc95c --- /dev/null +++ b/testloan.json @@ -0,0 +1,63 @@ +{ +"htmlTemplate": "statement.pdf.jinja", +"txtTemplate": "statement.txt.jinja", +"format": "html", +"email": { + "from_address": "jkent3rd@gmail.com", + "to_address": "jkent3rd@yahoo.com", + "server": "smtp.gmail.com", + "password": "pvyrbcnzrjoizprn", + "template": "./template.txt", + "send_pdf": "true", + "send_text": "true", + "subject": "Your loan statement...", + "body": "Your loan statement is attached.", + "text": "Please see your most recent account statement." + }, +"parameters": { + "account_number": "100100", + "principal": 10020.00, + "interest_rate": 6.5, + "periods_per_year": 12, + "periods": 18, + "start_date": "2017-12-05", + "start_interest_date": "2017-12-05", + "first_payment_month": "2018-01-15", + "monthly_payment": 589.00, + "payment_day_of_month": "15" + }, +"payments": [ + ["2018-01-16", "589.00"], + ["2018-03-30", "589.00"], + ["2018-04-06", "589.00"], + ["2018-04-12", "589.00"], + ["2018-05-28", "589.00"], + ["2018-06-12", "589.00"], + ["2018-07-13", "589.00"], + ["2018-08-14", "589.00"], + ["2018-09-26", "589.00"], + ["2018-10-15", "0"], + ["2018-11-29", "589.00"], + ["2018-12-31", "589.00"] + ], +"borrower": { + "name": "Bear Houses, LLC", + "address": "301 N Beauregard St Apt 203", + "city": "Alexandria", + "state": "VA", + "zip": "22312" + }, +"lender": { + "name": "John Kent", + "phone": "703.343.0782", + "address": "743 Madison St NW", + "city": "Washington", + "state": "DC", + "zip": "20011" + }, +"header": { + "title": "Installment Loan Statement", + "date": "Today" + } +} + From 4dae3256c532cb5b8d1a545959d04e0f48d213fa Mon Sep 17 00:00:00 2001 From: JohnKent Date: Tue, 5 Mar 2019 00:09:56 -0500 Subject: [PATCH 06/38] Updated the template to print the correct interest information. Fixed logic in script to send correct interest information to the template. Added ability to do a test run to a different email address with a real data file (the debug flag replaces the email address with a hard coded one). Added an aborting checking that the payments are listed in order. --- mortgage_template.py | 25 ++++++++++++++++++++----- statement.pdf.jinja | 2 +- testloan.json | 6 ++++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/mortgage_template.py b/mortgage_template.py index 69459c2..8565c46 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -114,6 +114,13 @@ def amortizeLoan(loan): 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 @@ -121,6 +128,7 @@ def amortizeLoan(loan): 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 @@ -143,6 +151,7 @@ def amortizeLoan(loan): 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 @@ -278,10 +287,14 @@ def main(): # at closing. The first payment will incur interest from one month before the bill is due. # read in the file + test_flag = True + #test_flag = False + test_address = 'jkent3rd@yahoo.com' + filename = "./testloan.json" - # filename = "./10Kloan.json" - # filename = "./dadmortgage.json" - # filename = "./brendamortgage.json" + #filename = "./10Kloan.json" + #filename = "./dadmortgage.json" + #filename = "./brendamortgage.json" #filename = "./greenfield_mortgage.json" template_filename = "statement.pdf.jinja" @@ -299,8 +312,10 @@ def main(): emailParameters = loan["email"] msg = generateEmail(emailParameters["from_address"], emailParameters["to_address"], emailParameters["subject"], emailParameters["body"], pdfAttachment, report) - sendEmail(msg, emailParameters["from_address"], emailParameters["to_address"], emailParameters['password']) - + if test_flag == False: + sendEmail(msg, emailParameters["from_address"], emailParameters["to_address"], emailParameters['password']) + else: + sendEmail(msg, emailParameters["from_address"], test_address, emailParameters['password']) if __name__ == '__main__': main() diff --git a/statement.pdf.jinja b/statement.pdf.jinja index 288e119..acd59c5 100644 --- a/statement.pdf.jinja +++ b/statement.pdf.jinja @@ -43,7 +43,7 @@ {{ "$%.2f"|format(item.interest_payment) }} {{ "$%.2f"|format(item.new_balance) }} - {% if item.month == '12' or loop.last %} + {% if item.month == 12 or loop.last %} Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}. {% endif %} {% endfor %} diff --git a/testloan.json b/testloan.json index f0cc95c..8a7331b 100644 --- a/testloan.json +++ b/testloan.json @@ -4,7 +4,7 @@ "format": "html", "email": { "from_address": "jkent3rd@gmail.com", - "to_address": "jkent3rd@yahoo.com", + "to_address": "jkent3rd@gmail.com", "server": "smtp.gmail.com", "password": "pvyrbcnzrjoizprn", "template": "./template.txt", @@ -38,7 +38,9 @@ ["2018-09-26", "589.00"], ["2018-10-15", "0"], ["2018-11-29", "589.00"], - ["2018-12-31", "589.00"] + ["2018-12-31", "589.00"], + ["2019-01-28", "589.00"], + ["2019-03-15", "589.00"] ], "borrower": { "name": "Bear Houses, LLC", From 42232903c53dca1fdf518f73d9bf268464ed61f7 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Tue, 5 Mar 2019 00:10:22 -0500 Subject: [PATCH 07/38] Updated the payment history. --- 10Kloan.json | 6 ++++-- brendamortgage.json | 6 ++++-- dadmortgage.json | 4 +++- greenfield_mortgage.json | 3 ++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/10Kloan.json b/10Kloan.json index b9cd72d..a731fea 100644 --- a/10Kloan.json +++ b/10Kloan.json @@ -38,8 +38,10 @@ ["2018-09-26", "589.00"], ["2018-10-15", "0"], ["2018-11-29", "589.00"], - ["2018-12-31", "589.00"] - ], + ["2018-12-31", "589.00"], + ["2019-01-15", "0.00"], + ["2019-02-13", "589.00"] +], "borrower": { "name": "Bear Houses, LLC", "address": "301 N Beauregard St Apt 203", diff --git a/brendamortgage.json b/brendamortgage.json index d13476f..b119644 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -4,7 +4,7 @@ "format": "html", "email": { "from_address": "jkent3rd@gmail.com", - "to_address": "bck@virginia.edu", + "to_address": "jkent3rd@gmail.com", "server": "smtp.gmail.com", "password": "pvyrbcnzrjoizprn", "template": "./template.txt", @@ -51,6 +51,8 @@ ["2018-10-01", "278.15"], ["2018-11-01", "278.15"], ["2018-12-01", "278.15"], - ["2019-01-01", "278.15"] + ["2019-01-01", "278.15"], + ["2019-02-01", "278.15"], + ["2019-03-01", "278.15"] ] } diff --git a/dadmortgage.json b/dadmortgage.json index 9ff0acb..60d3f41 100644 --- a/dadmortgage.json +++ b/dadmortgage.json @@ -51,6 +51,8 @@ ["2018-10-01", "278.15"], ["2018-11-01", "278.15"], ["2018-12-01", "278.15"], - ["2019-01-01", "278.15"] + ["2019-01-01", "278.15"], + ["2019-02-01", "278.15"], + ["2019-03-01", "278.15"] ] } diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json index c42064f..e2147da 100644 --- a/greenfield_mortgage.json +++ b/greenfield_mortgage.json @@ -40,7 +40,8 @@ ["2018-10-11", "803.00"], ["2018-11-13", "803.00"], ["2018-12-13", "803.00"], - ["2018-01-11", "803.00"] + ["2019-01-14", "803.00"], + ["2019-02-05", "803.00"] ], "borrower": { "name": "Bear Houses, LLC", From 8cb4be32486f71ba327e8b4942ffb43441700c26 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Wed, 3 Apr 2019 14:06:02 -0400 Subject: [PATCH 08/38] Updated the payment history for April statements. --- 10Kloan.json | 3 ++- brendamortgage.json | 3 ++- dadmortgage.json | 3 ++- greenfield_mortgage.json | 3 ++- mortgage_template.py | 8 ++++---- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/10Kloan.json b/10Kloan.json index a731fea..e7bb2f1 100644 --- a/10Kloan.json +++ b/10Kloan.json @@ -40,7 +40,8 @@ ["2018-11-29", "589.00"], ["2018-12-31", "589.00"], ["2019-01-15", "0.00"], - ["2019-02-13", "589.00"] + ["2019-02-13", "589.00"], + ["2019-03-15", "0"] ], "borrower": { "name": "Bear Houses, LLC", diff --git a/brendamortgage.json b/brendamortgage.json index b119644..8176c6c 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -53,6 +53,7 @@ ["2018-12-01", "278.15"], ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], - ["2019-03-01", "278.15"] + ["2019-03-01", "278.15"], + ["2019-04-01", "278.15"] ] } diff --git a/dadmortgage.json b/dadmortgage.json index 60d3f41..c4c35f0 100644 --- a/dadmortgage.json +++ b/dadmortgage.json @@ -53,6 +53,7 @@ ["2018-12-01", "278.15"], ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], - ["2019-03-01", "278.15"] + ["2019-03-01", "278.15"], + ["2019-04-01", "278.15"] ] } diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json index e2147da..d835dcd 100644 --- a/greenfield_mortgage.json +++ b/greenfield_mortgage.json @@ -41,7 +41,8 @@ ["2018-11-13", "803.00"], ["2018-12-13", "803.00"], ["2019-01-14", "803.00"], - ["2019-02-05", "803.00"] + ["2019-02-05", "803.00"], + ["2019-03-05", "803.00"] ], "borrower": { "name": "Bear Houses, LLC", diff --git a/mortgage_template.py b/mortgage_template.py index 8565c46..6d6d81f 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -279,6 +279,7 @@ def selectTemplate(loan): return template def main(): + test_flag = True # 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 @@ -287,14 +288,13 @@ def main(): # at closing. The first payment will incur interest from one month before the bill is due. # read in the file - test_flag = True - #test_flag = False + test_flag = False test_address = 'jkent3rd@yahoo.com' - filename = "./testloan.json" + #filename = "./testloan.json" #filename = "./10Kloan.json" #filename = "./dadmortgage.json" - #filename = "./brendamortgage.json" + filename = "./brendamortgage.json" #filename = "./greenfield_mortgage.json" template_filename = "statement.pdf.jinja" From 56c0f333f6574dd212436f4ef7570c51ad848fe0 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 2 Jun 2019 18:35:16 -0400 Subject: [PATCH 09/38] Updated the payment history. --- brendamortgage.json | 6 ++++-- dadmortgage.json | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/brendamortgage.json b/brendamortgage.json index 8176c6c..e79d4d0 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -4,7 +4,7 @@ "format": "html", "email": { "from_address": "jkent3rd@gmail.com", - "to_address": "jkent3rd@gmail.com", + "to_address": "bck@virginia.edu", "server": "smtp.gmail.com", "password": "pvyrbcnzrjoizprn", "template": "./template.txt", @@ -54,6 +54,8 @@ ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], ["2019-03-01", "278.15"], - ["2019-04-01", "278.15"] + ["2019-04-01", "278.15"], + ["2019-05-01", "278.15"], + ["2019-05-31", "278.15"] ] } diff --git a/dadmortgage.json b/dadmortgage.json index c4c35f0..107f7fa 100644 --- a/dadmortgage.json +++ b/dadmortgage.json @@ -54,6 +54,8 @@ ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], ["2019-03-01", "278.15"], - ["2019-04-01", "278.15"] + ["2019-04-01", "278.15"], + ["2019-05-01", "278.15"], + ["2019-05-31", "278.15"] ] } From 8b2af86db972b8c7fcdc8c5c18dc853047ea19b7 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sat, 6 Jul 2019 14:50:59 -0400 Subject: [PATCH 10/38] Updated the payment history. --- 10Kloan.json | 5 ++++- brendamortgage.json | 3 ++- dadmortgage.json | 3 ++- greenfield_mortgage.json | 7 +++++-- mortgage_template.py | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/10Kloan.json b/10Kloan.json index e7bb2f1..3bac0b5 100644 --- a/10Kloan.json +++ b/10Kloan.json @@ -41,7 +41,10 @@ ["2018-12-31", "589.00"], ["2019-01-15", "0.00"], ["2019-02-13", "589.00"], - ["2019-03-15", "0"] + ["2019-03-15", "0"], + ["2019-04-15", "0"], + ["2019-05-15", "589.00"], + ["2019-06-21", "2985.00"] ], "borrower": { "name": "Bear Houses, LLC", diff --git a/brendamortgage.json b/brendamortgage.json index e79d4d0..5b38944 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -56,6 +56,7 @@ ["2019-03-01", "278.15"], ["2019-04-01", "278.15"], ["2019-05-01", "278.15"], - ["2019-05-31", "278.15"] + ["2019-05-31", "278.15"], + ["2019-07-01", "278.15"] ] } diff --git a/dadmortgage.json b/dadmortgage.json index 107f7fa..f7ec1a1 100644 --- a/dadmortgage.json +++ b/dadmortgage.json @@ -56,6 +56,7 @@ ["2019-03-01", "278.15"], ["2019-04-01", "278.15"], ["2019-05-01", "278.15"], - ["2019-05-31", "278.15"] + ["2019-05-31", "278.15"], + ["2019-07-01", "278.15"] ] } diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json index d835dcd..89e1a59 100644 --- a/greenfield_mortgage.json +++ b/greenfield_mortgage.json @@ -19,7 +19,7 @@ "principal": 97750.00, "interest_rate": 5.5, "periods_per_year": 12, - "periods": 182, + "periods": 185, "start_date": "2017-11-07", "start_interest_date": "2017-11-11", "first_payment_month": "2017-12-15", @@ -42,7 +42,10 @@ ["2018-12-13", "803.00"], ["2019-01-14", "803.00"], ["2019-02-05", "803.00"], - ["2019-03-05", "803.00"] + ["2019-03-05", "803.00"], + ["2019-04-15", "803.00"], + ["2019-05-15", "0.00"], + ["2019-06-13", "803.00"] ], "borrower": { "name": "Bear Houses, LLC", diff --git a/mortgage_template.py b/mortgage_template.py index 6d6d81f..26bd2a8 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -292,9 +292,9 @@ def main(): test_address = 'jkent3rd@yahoo.com' #filename = "./testloan.json" - #filename = "./10Kloan.json" + filename = "./10Kloan.json" #filename = "./dadmortgage.json" - filename = "./brendamortgage.json" + #filename = "./brendamortgage.json" #filename = "./greenfield_mortgage.json" template_filename = "statement.pdf.jinja" From 6462ae562818b22150cd14f31229a1170c355ffc Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sat, 6 Jul 2019 14:58:43 -0400 Subject: [PATCH 11/38] Added options for the new 9K loan from Rivanna Graphite Investments, LLC. Removed the personal 10K loan I made to Bear Houses, LLC --- 9Kloan.json | 51 ++++++++++++++++++++++++++++++++++++++++++++ mortgage_template.py | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 9Kloan.json diff --git a/9Kloan.json b/9Kloan.json new file mode 100644 index 0000000..266d42a --- /dev/null +++ b/9Kloan.json @@ -0,0 +1,51 @@ +{ +"htmlTemplate": "statement.pdf.jinja", +"txtTemplate": "statement.txt.jinja", +"format": "html", +"email": { + "from_address": "jkent3rd@gmail.com", + "to_address": "grady@gradystreet.com", + "server": "smtp.gmail.com", + "password": "pvyrbcnzrjoizprn", + "template": "./template.txt", + "send_pdf": "true", + "send_text": "true", + "subject": "Your loan statement...", + "body": "Your loan statement is attached.", + "text": "Please see your most recent account statement." + }, +"parameters": { + "account_number": "100002", + "principal": 9000.00, + "interest_rate": 6.5, + "periods_per_year": 12, + "periods": 20, + "start_date": "2019-06-15", + "start_interest_date": "2019-06-17", + "first_payment_month": "2019-07-15", + "monthly_payment": 475.00, + "payment_day_of_month": "15" + }, +"payments": [ +], +"borrower": { + "name": "Bear Houses, LLC", + "address": "301 N Beauregard St Apt 203", + "city": "Alexandria", + "state": "VA", + "zip": "22312" + }, +"lender": { + "name": "Rivanna Graphite Investments, LLC", + "phone": "703.343.0782", + "address": "743 Madison St NW", + "city": "Washington", + "state": "DC", + "zip": "20011" + }, +"header": { + "title": "Installment Loan Statement", + "date": "Today" + } +} + diff --git a/mortgage_template.py b/mortgage_template.py index 26bd2a8..f31ae5d 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -292,7 +292,7 @@ def main(): test_address = 'jkent3rd@yahoo.com' #filename = "./testloan.json" - filename = "./10Kloan.json" + filename = "./9Kloan.json" #filename = "./dadmortgage.json" #filename = "./brendamortgage.json" #filename = "./greenfield_mortgage.json" From 27cf64aceda6db709bdd05c9f5ba8fef7c02f0ed Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 7 Jul 2019 14:58:20 -0400 Subject: [PATCH 12/38] Updated Brenda's contact information which has been wrong. --- brendamortgage.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/brendamortgage.json b/brendamortgage.json index 5b38944..2a9d7c7 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -34,12 +34,12 @@ "zip": "20011" }, "lender": { - "name": "John Kent", - "phone": "434-510-7272", - "address": "109 Shores Rd", - "city": "Palmyra", + "name": "Brenda Kelly", + "phone": "434-286-2110", + "address": "150 Confederate Street", + "city": "Scottsville", "state": "VA", - "zip": "22963" + "zip": "24590" }, "header": { "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA", From a30486970889feabe9716f80d91fd6fd0daa6abb Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 7 Jul 2019 14:59:33 -0400 Subject: [PATCH 13/38] 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: + + +
+
+

+
+ +
+ + + + + + + + + + + + + + + +
Loan Information
Lender:{{ model.lender.name }}
{{ model.lender.address }}
+ {{ model.lender.city }} {{model.lender.state }} {{ model.lender.zip }}
+ {{ model.lender.phone }} +
Borrower:{{ model.borrower.name }} 
{{ model.borrower.address }}  +
{{ model.borrower.city }}, {{model.borrower.state }} {{ model.borrower.zip }} +
Account Number:{{ model.parameters.account_number }}
Origination Date: {{ model.parameters.start_date }}
Original Principal:{{ "$%.2f"|format(model.parameters.principal) }}
Rate:{{model.parameters.interest_rate }}%
Term: {{model.parameters.periods }} months
Next Payment Due Date: {{model.parameters.next_due_date}}
Payment Due: {{ "$%.2f"|format(model.parameters.next_payment_amt) }}
+

+

+
+ + + + + + + + + + + + + + + + + {% for item in model.past_payments %} + + + + + + + + + + + {% if item.month == 12 or loop.last %} + + {% endif %} + {% endfor %} + + +
Loan History
# + Due DateDate PaidDays InterestPayment AmtPrincipal PmtInterest PmtNew Balance
{{ 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) }}
Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.
Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.
+
+
+ + + + + + + + + + + + + + + + + {% for item in model.future_payments %} + + + + + + + + + {% endfor %} + Balloon Payment Due: {{ "$%.2f"|format(model.balloon_payment) }} + +
Remaining Amortization
#Due DateDays InterestPayment AmtPrincipal PmtInterest PmtPrincipal Balance
{{ 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) }}
+
+
+
+ + + + + + + + + + +
Generate and Send Statement
Send From:{{model.email.from_address}}
Send To:{{model.email.to_address}}
Topic:
Message Body:
Send Statement As:
Include Future Amortization
+ +
+
+
+

Add Loan Payment Tab

+
+
+ + \ 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 From e9fefb1a1abd51871a984ae7994dc36243995475 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Mon, 8 Jul 2019 00:27:38 -0400 Subject: [PATCH 14/38] Web version updated to allow sending emails. --- templates/add.html | 18 +++ templates/email.html | 19 +++ templates/main.html | 35 ++--- templates/statement.html.jinja | 84 +++++++++++ .../statement.pdf.jinja | 6 +- .../statement.text.jinja | 0 web.py | 134 +++++++++++++++++- 7 files changed, 273 insertions(+), 23 deletions(-) create mode 100644 templates/add.html create mode 100644 templates/email.html create mode 100644 templates/statement.html.jinja rename statement.pdf.jinja => templates/statement.pdf.jinja (99%) rename statement.txt.jinja => templates/statement.text.jinja (100%) diff --git a/templates/add.html b/templates/add.html new file mode 100644 index 0000000..7e68337 --- /dev/null +++ b/templates/add.html @@ -0,0 +1,18 @@ + + + + + + + + Loan Management + + + + + +Return to Main Screen. + + \ No newline at end of file diff --git a/templates/email.html b/templates/email.html new file mode 100644 index 0000000..530e273 --- /dev/null +++ b/templates/email.html @@ -0,0 +1,19 @@ + + + + + + + + Loan Management + + + + +

The email has been sent.

+Return to Main Screen. + + + \ No newline at end of file diff --git a/templates/main.html b/templates/main.html index 674a873..b311656 100644 --- a/templates/main.html +++ b/templates/main.html @@ -10,7 +10,7 @@ Loan Management -

Web Mortgage Manager

+
@@ -131,21 +131,24 @@
-
Loan:
- - - - - - - - +
Generate and Send Statement
Send From:{{model.email.from_address}}
Send To:{{model.email.to_address}}
Topic:
Message Body:
Send Statement As:
Include Future Amortization
+ + + + + + + + + +
Generate and Send Statement
Send From: {{model.email.from_address}}
Send To: {{model.email.to_address}}
Subject:
Message:
Send Statement As:
+ HTML + PDF + Plain Text +
Include Future Amortization
+ Yes + No +
diff --git a/templates/statement.html.jinja b/templates/statement.html.jinja new file mode 100644 index 0000000..48e124c --- /dev/null +++ b/templates/statement.html.jinja @@ -0,0 +1,84 @@ + + +

{{ model.header.title }}

+

{{ model.lender.name }}

+ +

{{ model.lender.phone }} - {{ model.lender.address }} - +{{ model.lender.city }} {{model.lender.state }} {{ model.lender.zip }}

+

Statement Date: {{ model.header.date }}

+ +

+ + + + + + + + + + +
Loan Information 
Borrower: {{ model.borrower.name }}  Account Number: {{ model.parameters.account_number }}
{{ model.borrower.address }}  Origination Date: {{ model.parameters.start_date }}
{{ model.borrower.city }}, {{model.borrower.state }} {{ model.borrower.zip }}Original Principal: {{ "$%.2f"|format(model.parameters.principal) }}
Rate: {{model.parameters.interest_rate }}% Term: {{model.parameters.periods }} months
Next Payment Due Date: {{model.parameters.next_due_date}} Payment Due: {{ "$%.2f"|format(model.parameters.next_payment_amt) }}
+

+

Payment History

+ + + + + + + + + + + +{% for item in model.past_payments %} + + + + + + + + + + {% if item.month == 12 or loop.last %} + + {% endif %} +{% endfor %} + + +
# + Due DateDate PaidDays InterestPayment AmtPrincipal PmtInterest PmtNew Balance
{{ 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) }}
Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.
Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.
+

+ +

Remaining Amortization

+ + + + + + + + + + + + + +{% for item in model.future_payments %} + + + + + + + + +{% endfor %} + +
#Due DateDays InterestPayment AmtPrincipal PmtInterest PmtPrincipal Balance
{{ 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) }}
+

Balloon Payment Due: {{ "$%.2f"|format(model.balloon_payment) }}

+
+ + \ No newline at end of file diff --git a/statement.pdf.jinja b/templates/statement.pdf.jinja similarity index 99% rename from statement.pdf.jinja rename to templates/statement.pdf.jinja index acd59c5..48e124c 100644 --- a/statement.pdf.jinja +++ b/templates/statement.pdf.jinja @@ -54,7 +54,8 @@

Remaining Amortization

- + + @@ -62,7 +63,8 @@ - + + {% for item in model.future_payments %} diff --git a/statement.txt.jinja b/templates/statement.text.jinja similarity index 100% rename from statement.txt.jinja rename to templates/statement.text.jinja diff --git a/web.py b/web.py index 672499d..d1a2361 100644 --- a/web.py +++ b/web.py @@ -2,9 +2,13 @@ from flask import Flask, render_template, request, redirect, url_for import json from decimal import * from datetime import * -from jinja2 import Environment, FileSystemLoader +import jinja2 from fpdf import FPDF, HTMLMixin 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.MIMEText import MIMEText @@ -21,6 +25,7 @@ def hello(): 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") ) + loans.append( addLoan("5 Test Loan", "testloan.json")) if 'loan' in request.args: filename = request.args["loan"] @@ -34,12 +39,49 @@ def hello(): @app.route('/update_file') def update_file(): - return + loanFile = request.form['loan'] + return redirect( '/?loan=' + loan ) @app.route('/send_statement', methods=['POST']) def send_statement(): - loan = request.form["loan"] - redirect( '/?loan=' + loan ) + loanFile = request.form["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): x = {} @@ -47,7 +89,10 @@ def addLoan(loanName, fileName): x['filename'] = fileName return x -'''from old code''' +################### +# from old code # +################### + def getStatementHeader(datastore): return datastore['header'] @@ -238,6 +283,85 @@ def amortizeLoan(loan): loan["future_payments"] = future_payments 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__': app.debug = True From 717210636d35ee357023b9daadb77745caf11815 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Wed, 10 Jul 2019 22:57:23 -0400 Subject: [PATCH 15/38] The tool can now update the payment history. --- mortgage_template.py | 26 ++++++- templates/add.html | 6 +- templates/main.html | 14 +++- testloan.json | 167 ++++++++++++++++++++++++++++--------------- 4 files changed, 150 insertions(+), 63 deletions(-) diff --git a/mortgage_template.py b/mortgage_template.py index f31ae5d..dfa8260 100644 --- a/mortgage_template.py +++ b/mortgage_template.py @@ -203,7 +203,7 @@ def amortizeLoan(loan): def transformTemplate(template_fileName, loanModel): - # template_filename = "statement.txt.jinja" + # template_filename = "statement.text.jinja" # setup jinja for creating the statement env = Environment(loader=FileSystemLoader('.')) template = env.get_template(template_fileName) @@ -242,6 +242,30 @@ def generateEmail(from_address, to_address, subject, body, pdfAttachment, txtAtt return msg +def generateEmailWithAttachments(from_address, to_address, subject, body, pdfAttachment, htmlAttachment, txtAttachment): + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = from_address + msg['To'] = to_address + + msg.attach(MIMEText(body)) + + if (pdfAttachment != None): + part = MIMEBase("application", "octet-stream") + part.set_payload(pdfAttachment) + Encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"') + msg.attach(part) + + if (txtAttachment != None): + part = MIMEBase("text", "html") + part.set_payload(txtAttachment) + Encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="statement.html"') + msg.attach(part) + + return msg + def sendEmail(msg, from_address, to_address, passwd): try: diff --git a/templates/add.html b/templates/add.html index 7e68337..78323fd 100644 --- a/templates/add.html +++ b/templates/add.html @@ -12,7 +12,11 @@ - +
# Due Date Days InterestPrincipal Pmt Interest Pmt Principal Balance
{{ item.payment_number }}
+{% for message in messages %} + +{% endfor %} +
{{message}}
Return to Main Screen. \ No newline at end of file diff --git a/templates/main.html b/templates/main.html index b311656..9bc9409 100644 --- a/templates/main.html +++ b/templates/main.html @@ -151,11 +151,21 @@ - +
-

Add Loan Payment Tab

+
+ + + + + + + + +
Record a Payment
Payment Date:
Payment Amount:
+
diff --git a/testloan.json b/testloan.json index 8a7331b..731c04d 100644 --- a/testloan.json +++ b/testloan.json @@ -1,65 +1,114 @@ { -"htmlTemplate": "statement.pdf.jinja", -"txtTemplate": "statement.txt.jinja", -"format": "html", -"email": { - "from_address": "jkent3rd@gmail.com", - "to_address": "jkent3rd@gmail.com", - "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn", - "template": "./template.txt", - "send_pdf": "true", - "send_text": "true", - "subject": "Your loan statement...", - "body": "Your loan statement is attached.", - "text": "Please see your most recent account statement." + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 589.0, + "interest_rate": 6.5, + "start_interest_date": "2017-12-05", + "payment_day_of_month": "15", + "first_payment_month": "2018-01-15", + "account_number": "100100", + "principal": 10020.0, + "start_date": "2017-12-05", + "periods_per_year": 12, + "periods": 18 }, -"parameters": { - "account_number": "100100", - "principal": 10020.00, - "interest_rate": 6.5, - "periods_per_year": 12, - "periods": 18, - "start_date": "2017-12-05", - "start_interest_date": "2017-12-05", - "first_payment_month": "2018-01-15", - "monthly_payment": 589.00, - "payment_day_of_month": "15" + "format": "html", + "txtTemplate": "statement.txt.jinja", + "lender": { + "city": "Washington", + "name": "Test Lender", + "zip": "20011", + "phone": "703.343.0782", + "state": "DC", + "address": "743 Madison St NW" }, -"payments": [ - ["2018-01-16", "589.00"], - ["2018-03-30", "589.00"], - ["2018-04-06", "589.00"], - ["2018-04-12", "589.00"], - ["2018-05-28", "589.00"], - ["2018-06-12", "589.00"], - ["2018-07-13", "589.00"], - ["2018-08-14", "589.00"], - ["2018-09-26", "589.00"], - ["2018-10-15", "0"], - ["2018-11-29", "589.00"], - ["2018-12-31", "589.00"], - ["2019-01-28", "589.00"], - ["2019-03-15", "589.00"] - ], -"borrower": { - "name": "Bear Houses, LLC", - "address": "301 N Beauregard St Apt 203", - "city": "Alexandria", - "state": "VA", - "zip": "22312" + "header": { + "date": "Today", + "title": "Installment Loan Test Statement" }, -"lender": { - "name": "John Kent", - "phone": "703.343.0782", - "address": "743 Madison St NW", - "city": "Washington", - "state": "DC", - "zip": "20011" + "payments": [ + [ + "2018-01-16", + "589.00" + ], + [ + "2018-03-30", + "589.00" + ], + [ + "2018-04-06", + "589.00" + ], + [ + "2018-04-12", + "589.00" + ], + [ + "2018-05-28", + "589.00" + ], + [ + "2018-06-12", + "589.00" + ], + [ + "2018-07-13", + "589.00" + ], + [ + "2018-08-14", + "589.00" + ], + [ + "2018-09-26", + "589.00" + ], + [ + "2018-10-15", + "0" + ], + [ + "2018-11-29", + "589.00" + ], + [ + "2018-12-31", + "589.00" + ], + [ + "2019-01-28", + "589.00" + ], + [ + "2019-03-15", + "589.00" + ], + [ + "2019-07-10", + "123.12" + ], + [ + "2019-07-10", + "120" + ] + ], + "borrower": { + "city": "Alexandria", + "state": "VA", + "name": "Test Borrower Houses, LLC", + "zip": "22312", + "address": "301 N Beauregard St Apt 203" }, -"header": { - "title": "Installment Loan Statement", - "date": "Today" + "email": { + "body": "Your test loan statement is attached.", + "to_address": "jkent3rd@gmail.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent test account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "pvyrbcnzrjoizprn", + "subject": "Your test loan statement..." } -} - +} \ No newline at end of file From af033464ad8c6986c61f8c381989552e97f863fa Mon Sep 17 00:00:00 2001 From: JohnKent Date: Wed, 10 Jul 2019 22:57:55 -0400 Subject: [PATCH 16/38] The tool can now update payment history. --- web.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/web.py b/web.py index d1a2361..6db08ac 100644 --- a/web.py +++ b/web.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, request, redirect, url_for +from flask import Flask, render_template, request, redirect import json from decimal import * from datetime import * @@ -32,15 +32,72 @@ def hello(): else: return redirect('/?loan=' + loans[0]['filename']) - loan = loadLoanInformation('/Users/john/PycharmProjects/mortgage/' + filename) + loan = loadLoanInformation(getFullPathofLoanFile(filename)) amortizeLoan(loan) return render_template('main.html', filename=filename, loans=loans, model=loan) -@app.route('/update_file') +@app.route('/update_file', methods=['POST']) def update_file(): - loanFile = request.form['loan'] - return redirect( '/?loan=' + loan ) + + messages = [] + + loanFile = request.form["loan"] + data = getDatastore(getFullPathofLoanFile(loanFile)) + + payment_history = data["payments"] + + if 'date' in request.form: + if (request.form['date'] == ''): + now = datetime.now() + payment_date = str(now.strftime("%Y-%m-%d")) + messages.append("No date was provided. Assuming today's date of " + payment_date + ".") + else: + payment_date = request.form['date'] + messages.append("Date provided: " + payment_date + ".") + else: + now = datetime.now() + payment_date = str(now.strftime("%Y-%m-%d")) + + payment_amount = Decimal('0.00') + proceed_flag = True + if 'amount' in request.form: + if (request.form['amount'] != ''): + try: + payment_amount = Decimal(request.form['amount']) + messages.append("Amount provided: " + str(payment_amount) + ".") + except: + payment_amount = Decimal('0.00') + messages.append("The amount provided could not be interpreted. Your payment was not recorded.") + proceed_flag = False + else: + pass + + if proceed_flag is True: + try: + backup_filename = loanFile + ".backup-" + datetime.now().strftime("%Y-%m-%d %H-%M-%S") + backup_file = open(getFullPathofLoanFile(backup_filename), 'w+') + json.dump(data, backup_file) + backup_file.close() + except: + messages.append("A backup file could not be created. Your payment was not recorded.") + proceed_flag = False + else: + messages.append("A backup of your file was created: '" + backup_filename +"'" ) + + if proceed_flag is True: + try: + payment_history.append( [payment_date, str(payment_amount)]) + file = open(getFullPathofLoanFile(loanFile), 'w+') + json.dump(data, file) + file.close() + except: + messages.append("An error occurred writing to the file. Your payment file may be corrupt, " + \ + "please consider rolling back to the backup created above.") + else: + messages.append("The payment was successfully written. ") + + return render_template('add.html', filename=loanFile, messages=messages) @app.route('/send_statement', methods=['POST']) def send_statement(): @@ -49,7 +106,7 @@ def send_statement(): message = request.form["message"] style = request.form["style"] - loan = loadLoanInformation('/Users/john/PycharmProjects/mortgage/' + loanFile) + loan = loadLoanInformation(getFullPathofLoanFile(loanFile)) amortizeLoan(loan) reportCreated = False @@ -89,6 +146,9 @@ def addLoan(loanName, fileName): x['filename'] = fileName return x +def getFullPathofLoanFile(filename): + return '/Users/john/PycharmProjects/mortgage/' + filename + ################### # from old code # ################### From 8bc23dc8b103b967bfd7f41dcc773dcb61c18fe3 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Wed, 10 Jul 2019 23:12:21 -0400 Subject: [PATCH 17/38] Fixed a problem in email sending when attach/embed flag was removed. --- testloan.json | 13 +++---------- web.py | 3 --- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/testloan.json b/testloan.json index 731c04d..336c69c 100644 --- a/testloan.json +++ b/testloan.json @@ -1,5 +1,4 @@ { - "htmlTemplate": "statement.pdf.jinja", "parameters": { "monthly_payment": 589.0, "interest_rate": 6.5, @@ -12,8 +11,6 @@ "periods_per_year": 12, "periods": 18 }, - "format": "html", - "txtTemplate": "statement.txt.jinja", "lender": { "city": "Washington", "name": "Test Lender", @@ -100,15 +97,11 @@ "address": "301 N Beauregard St Apt 203" }, "email": { - "body": "Your test loan statement is attached.", "to_address": "jkent3rd@gmail.com", "from_address": "jkent3rd@gmail.com", - "text": "Please see your most recent test account statement.", + "subject": "Your test loan statement...", + "body": "Your test loan statement is attached.", "server": "smtp.gmail.com", - "send_text": "true", - "template": "./template.txt", - "send_pdf": "true", - "password": "pvyrbcnzrjoizprn", - "subject": "Your test loan statement..." + "password": "pvyrbcnzrjoizprn" } } \ No newline at end of file diff --git a/web.py b/web.py index 6db08ac..f75db46 100644 --- a/web.py +++ b/web.py @@ -104,7 +104,6 @@ def send_statement(): loanFile = request.form["loan"] subject = request.form["subject"] message = request.form["message"] - style = request.form["style"] loan = loadLoanInformation(getFullPathofLoanFile(loanFile)) amortizeLoan(loan) @@ -119,8 +118,6 @@ def send_statement(): 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): From 3ffbd5ab3d0b2f90781a88adc30dbac2aeb61bfd Mon Sep 17 00:00:00 2001 From: JohnKent Date: Sun, 26 Jan 2020 14:02:44 -0500 Subject: [PATCH 18/38] Created a source directly for purposes of deployment as zip file. Updated to Python 3. --- 9Kloan.json | 52 +------------- brendamortgage.json | 63 +---------------- dadmortgage.json | 63 +---------------- greenfield_mortgage.json | 70 +------------------ .../mortgage_template.py | 0 {templates => mortgage/templates}/add.html | 0 {templates => mortgage/templates}/email.html | 0 {templates => mortgage/templates}/main.html | 11 ++- .../templates}/statement.html.jinja | 0 .../templates}/statement.pdf.jinja | 0 .../templates}/statement.text.jinja | 0 web.py => mortgage/web.py | 36 +++++----- 12 files changed, 33 insertions(+), 262 deletions(-) rename mortgage_template.py => mortgage/mortgage_template.py (100%) rename {templates => mortgage/templates}/add.html (100%) rename {templates => mortgage/templates}/email.html (100%) rename {templates => mortgage/templates}/main.html (97%) rename {templates => mortgage/templates}/statement.html.jinja (100%) rename {templates => mortgage/templates}/statement.pdf.jinja (100%) rename {templates => mortgage/templates}/statement.text.jinja (100%) rename web.py => mortgage/web.py (96%) diff --git a/9Kloan.json b/9Kloan.json index 266d42a..d54e2af 100644 --- a/9Kloan.json +++ b/9Kloan.json @@ -1,51 +1 @@ -{ -"htmlTemplate": "statement.pdf.jinja", -"txtTemplate": "statement.txt.jinja", -"format": "html", -"email": { - "from_address": "jkent3rd@gmail.com", - "to_address": "grady@gradystreet.com", - "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn", - "template": "./template.txt", - "send_pdf": "true", - "send_text": "true", - "subject": "Your loan statement...", - "body": "Your loan statement is attached.", - "text": "Please see your most recent account statement." - }, -"parameters": { - "account_number": "100002", - "principal": 9000.00, - "interest_rate": 6.5, - "periods_per_year": 12, - "periods": 20, - "start_date": "2019-06-15", - "start_interest_date": "2019-06-17", - "first_payment_month": "2019-07-15", - "monthly_payment": 475.00, - "payment_day_of_month": "15" - }, -"payments": [ -], -"borrower": { - "name": "Bear Houses, LLC", - "address": "301 N Beauregard St Apt 203", - "city": "Alexandria", - "state": "VA", - "zip": "22312" - }, -"lender": { - "name": "Rivanna Graphite Investments, LLC", - "phone": "703.343.0782", - "address": "743 Madison St NW", - "city": "Washington", - "state": "DC", - "zip": "20011" - }, -"header": { - "title": "Installment Loan Statement", - "date": "Today" - } -} - +{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 475.0, "interest_rate": 6.5, "start_interest_date": "2019-06-17", "payment_day_of_month": "15", "first_payment_month": "2019-07-15", "account_number": "100002", "periods": 20, "start_date": "2019-06-15", "periods_per_year": 12, "principal": 9000.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "grady@gradystreet.com", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Washington", "name": "Rivanna Graphite Investments, LLC", "zip": "20011", "phone": "703.343.0782", "state": "DC", "address": "743 Madison St NW"}, "header": {"date": "Today", "title": "Installment Loan Statement"}, "payments": [["2019-07-15", "475.00"], ["2019-08-14", "475.00"], ["2019-09-17", "475"], ["2019-10-15", "0"], ["2019-11-15", "0"], ["2019-12-5", "475"]], "borrower": {"city": "Alexandria", "state": "VA", "name": "Bear Houses, LLC", "zip": "22312", "address": "301 N Beauregard St Apt 203"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file diff --git a/brendamortgage.json b/brendamortgage.json index 2a9d7c7..948cec0 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -1,62 +1 @@ -{ -"htmlTemplate": "statement.pdf.jinja", -"txtTemplate": "statement.txt.jinja", -"format": "html", -"email": { - "from_address": "jkent3rd@gmail.com", - "to_address": "bck@virginia.edu", - "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn", - "template": "./template.txt", - "send_pdf": "true", - "send_text": "true", - "subject": "Your loan statement...", - "body": "Your loan statement is attached.", - "text": "Please see your most recent account statement." - }, -"parameters": { - "account_number": "100001", - "principal": 35000.00, - "interest_rate": 5.0, - "periods_per_year": 12, - "periods": 180, - "start_date": "2018-06-25", - "start_interest_date": "2018-07-01", - "first_payment_month": "2018-08-01", - "monthly_payment": 278.15, - "payment_day_of_month": "01" - }, -"borrower": { - "name": "Grandma Tina's Properties, LLC", - "address": "743 Madison St NW", - "city": "Washington", - "state": "DC", - "zip": "20011" - }, -"lender": { - "name": "Brenda Kelly", - "phone": "434-286-2110", - "address": "150 Confederate Street", - "city": "Scottsville", - "state": "VA", - "zip": "24590" - }, -"header": { - "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA", - "date": "Today" - }, -"payments": [ - ["2018-08-01", "278.15"], - ["2018-09-01", "278.15"], - ["2018-10-01", "278.15"], - ["2018-11-01", "278.15"], - ["2018-12-01", "278.15"], - ["2019-01-01", "278.15"], - ["2019-02-01", "278.15"], - ["2019-03-01", "278.15"], - ["2019-04-01", "278.15"], - ["2019-05-01", "278.15"], - ["2019-05-31", "278.15"], - ["2019-07-01", "278.15"] - ] -} +{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 278.15, "interest_rate": 5.0, "start_interest_date": "2018-07-01", "payment_day_of_month": "01", "first_payment_month": "2018-08-01", "account_number": "100001", "periods": 180, "start_date": "2018-06-25", "periods_per_year": 12, "principal": 35000.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "bck@virginia.edu", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Scottsville", "name": "Brenda Kelly", "zip": "24590", "phone": "434-286-2110", "state": "VA", "address": "150 Confederate Street"}, "header": {"date": "Today", "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA"}, "payments": [["2018-08-01", "278.15"], ["2018-09-01", "278.15"], ["2018-10-01", "278.15"], ["2018-11-01", "278.15"], ["2018-12-01", "278.15"], ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], ["2019-03-01", "278.15"], ["2019-04-01", "278.15"], ["2019-05-01", "278.15"], ["2019-05-31", "278.15"], ["2019-07-01", "278.15"], ["2019-08-01", "278.15"], ["2019-09-02", "278.15"], ["2019-10-01", "278.15"], ["2019-11-01", "278.15"], ["2019-12-02", "278.15"], ["2020-01-02", "278.15"]], "borrower": {"city": "Washington", "state": "DC", "name": "Grandma Tina's Properties, LLC", "zip": "20011", "address": "743 Madison St NW"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file diff --git a/dadmortgage.json b/dadmortgage.json index f7ec1a1..933f1ee 100644 --- a/dadmortgage.json +++ b/dadmortgage.json @@ -1,62 +1 @@ -{ -"htmlTemplate": "statement.pdf.jinja", -"txtTemplate": "statement.txt.jinja", -"format": "html", -"email": { - "from_address": "jkent3rd@gmail.com", - "to_address": "johnkent49@gmail.com", - "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn", - "template": "./template.txt", - "send_pdf": "true", - "send_text": "true", - "subject": "Your loan statement...", - "body": "Your loan statement is attached.", - "text": "Please see your most recent account statement." - }, -"parameters": { - "account_number": "100001", - "principal": 35000.00, - "interest_rate": 5.0, - "periods_per_year": 12, - "periods": 180, - "start_date": "2018-06-25", - "start_interest_date": "2018-07-01", - "first_payment_month": "2018-08-01", - "monthly_payment": 278.15, - "payment_day_of_month": "01" - }, -"borrower": { - "name": "Grandma Tina's Properties, LLC", - "address": "743 Madison St NW", - "city": "Washington", - "state": "DC", - "zip": "20011" - }, -"lender": { - "name": "John Kent", - "phone": "434-510-7272", - "address": "109 Shores Rd", - "city": "Palmyra", - "state": "VA", - "zip": "22963" - }, -"header": { - "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA", - "date": "Today" - }, -"payments": [ - ["2018-08-01", "278.15"], - ["2018-09-01", "278.15"], - ["2018-10-01", "278.15"], - ["2018-11-01", "278.15"], - ["2018-12-01", "278.15"], - ["2019-01-01", "278.15"], - ["2019-02-01", "278.15"], - ["2019-03-01", "278.15"], - ["2019-04-01", "278.15"], - ["2019-05-01", "278.15"], - ["2019-05-31", "278.15"], - ["2019-07-01", "278.15"] - ] -} +{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 278.15, "interest_rate": 5.0, "start_interest_date": "2018-07-01", "payment_day_of_month": "01", "first_payment_month": "2018-08-01", "account_number": "100001", "periods": 180, "start_date": "2018-06-25", "periods_per_year": 12, "principal": 35000.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "johnkent49@gmail.com", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Palmyra", "name": "John Kent", "zip": "22963", "phone": "434-510-7272", "state": "VA", "address": "109 Shores Rd"}, "header": {"date": "Today", "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA"}, "payments": [["2018-08-01", "278.15"], ["2018-09-01", "278.15"], ["2018-10-01", "278.15"], ["2018-11-01", "278.15"], ["2018-12-01", "278.15"], ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], ["2019-03-01", "278.15"], ["2019-04-01", "278.15"], ["2019-05-01", "278.15"], ["2019-05-31", "278.15"], ["2019-07-01", "278.15"], ["2019-08-01", "278.15"], ["2019-09-02", "278.15"], ["2019-10-01", "278.15"], ["2019-11-01", "278.15"], ["2019-12-02", "278.15"], ["2020-01-02", "278.15"]], "borrower": {"city": "Washington", "state": "DC", "name": "Grandma Tina's Properties, LLC", "zip": "20011", "address": "743 Madison St NW"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json index 89e1a59..e278296 100644 --- a/greenfield_mortgage.json +++ b/greenfield_mortgage.json @@ -1,69 +1 @@ -{ -"htmlTemplate": "statement.pdf.jinja", -"txtTemplate": "statement.txt.jinja", -"format": "html", -"email": { - "from_address": "jkent3rd@gmail.com", - "to_address": "grady@gradystreet.com", - "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn", - "template": "./template.txt", - "send_pdf": "true", - "send_text": "true", - "subject": "Your loan statement...", - "body": "Your loan statement is attached.", - "text": "Please see your most recent account statement." - }, -"parameters": { - "account_number": "100001", - "principal": 97750.00, - "interest_rate": 5.5, - "periods_per_year": 12, - "periods": 185, - "start_date": "2017-11-07", - "start_interest_date": "2017-11-11", - "first_payment_month": "2017-12-15", - "monthly_payment": 803.00, - "payment_day_of_month": "15" - }, -"payments": [ - ["2017-12-11", "803.00"], - ["2018-01-23", "803.00"], - ["2018-03-23", "803.00"], - ["2018-04-18", "803.00"], - ["2018-04-26", "803.00"], - ["2018-05-15", "0.00"], - ["2018-06-15", "0.00"], - ["2018-07-12", "803.00"], - ["2018-08-07", "803.00"], - ["2018-09-06", "803.00"], - ["2018-10-11", "803.00"], - ["2018-11-13", "803.00"], - ["2018-12-13", "803.00"], - ["2019-01-14", "803.00"], - ["2019-02-05", "803.00"], - ["2019-03-05", "803.00"], - ["2019-04-15", "803.00"], - ["2019-05-15", "0.00"], - ["2019-06-13", "803.00"] - ], -"borrower": { - "name": "Bear Houses, LLC", - "address": "301 N Beauregard St Apt 203", - "city": "Alexandria", - "state": "VA", - "zip": "22312" - }, -"lender": { - "name": "Rivanna Graphite Investments, LLC", - "phone": "703.343.0782", - "address": "743 Madison St NW", - "city": "Washington", - "state": "DC", - "zip": "20011" - }, -"header": { - "title": "Mortgage Loan Statement - 195 Greenfield Lane, Pearl MS", - "date": "Today" - } -} +{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 803.0, "interest_rate": 5.5, "start_interest_date": "2017-11-11", "payment_day_of_month": "15", "first_payment_month": "2017-12-15", "account_number": "100001", "periods": 185, "start_date": "2017-11-07", "periods_per_year": 12, "principal": 97750.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "grady@gradystreet.com", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Washington", "name": "Rivanna Graphite Investments, LLC", "zip": "20011", "phone": "703.343.0782", "state": "DC", "address": "743 Madison St NW"}, "header": {"date": "Today", "title": "Mortgage Loan Statement - 195 Greenfield Lane, Pearl MS"}, "payments": [["2017-12-11", "803.00"], ["2018-01-23", "803.00"], ["2018-03-23", "803.00"], ["2018-04-18", "803.00"], ["2018-04-26", "803.00"], ["2018-05-15", "0.00"], ["2018-06-15", "0.00"], ["2018-07-12", "803.00"], ["2018-08-07", "803.00"], ["2018-09-06", "803.00"], ["2018-10-11", "803.00"], ["2018-11-13", "803.00"], ["2018-12-13", "803.00"], ["2019-01-14", "803.00"], ["2019-02-05", "803.00"], ["2019-03-05", "803.00"], ["2019-04-15", "803.00"], ["2019-05-15", "0.00"], ["2019-06-13", "803.00"], ["2019-07-25", "803.00"], ["2019-08-14", "803.00"], ["2019-09-13", "803.00"], ["2019-10-21", "803.00"], ["2019-11-29", "803.00"], ["2019-12-19", "803.00"], ["2020-01-15", "803"]], "borrower": {"city": "Alexandria", "state": "VA", "name": "Bear Houses, LLC", "zip": "22312", "address": "301 N Beauregard St Apt 203"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file diff --git a/mortgage_template.py b/mortgage/mortgage_template.py similarity index 100% rename from mortgage_template.py rename to mortgage/mortgage_template.py diff --git a/templates/add.html b/mortgage/templates/add.html similarity index 100% rename from templates/add.html rename to mortgage/templates/add.html diff --git a/templates/email.html b/mortgage/templates/email.html similarity index 100% rename from templates/email.html rename to mortgage/templates/email.html diff --git a/templates/main.html b/mortgage/templates/main.html similarity index 97% rename from templates/main.html rename to mortgage/templates/main.html index 9bc9409..9edc972 100644 --- a/templates/main.html +++ b/mortgage/templates/main.html @@ -15,7 +15,7 @@
Loan: - {% for loan in loans %} {% if loan.filename==filename %} @@ -28,6 +28,15 @@
+

    diff --git a/templates/statement.html.jinja b/mortgage/templates/statement.html.jinja similarity index 100% rename from templates/statement.html.jinja rename to mortgage/templates/statement.html.jinja diff --git a/templates/statement.pdf.jinja b/mortgage/templates/statement.pdf.jinja similarity index 100% rename from templates/statement.pdf.jinja rename to mortgage/templates/statement.pdf.jinja diff --git a/templates/statement.text.jinja b/mortgage/templates/statement.text.jinja similarity index 100% rename from templates/statement.text.jinja rename to mortgage/templates/statement.text.jinja diff --git a/web.py b/mortgage/web.py similarity index 96% rename from web.py rename to mortgage/web.py index f75db46..90da3df 100644 --- a/web.py +++ b/mortgage/web.py @@ -10,11 +10,10 @@ 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.MIMEText import MIMEText -from email.MIMEBase import MIMEBase -from email import Encoders - +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.base import MIMEBase +from email import encoders app = Flask(__name__) @app.route('/') @@ -218,9 +217,9 @@ def getDatastore(filename=None): datastore = json.load(f) except Exception as e: - print "An error occurred opening your loan file '%s'. " % filename - print "The Exception:" - print e.__repr__() + print("An error occurred opening your loan file '%s'. " % filename) + print("The Exception:") + print(e.__repr__()) quit() return datastore @@ -256,8 +255,8 @@ def amortizeLoan(loan): #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) + 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")) @@ -345,7 +344,7 @@ def transformTemplate(template_fileName, loanModel): # setup jinja for creating the statement template = environment.get_template(template_fileName) - print loanModel + print(loanModel) report = template.render(model=loanModel) return report @@ -361,21 +360,21 @@ def generateEmail(from_address, to_address, subject, body, pdf, html, txt): if (pdf != None): part = MIMEBase("application", "octet-stream") part.set_payload(pdf) - Encoders.encode_base64(part) + 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) + 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) + encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="statement.txt"') msg.attach(part) @@ -392,7 +391,7 @@ def sendEmail(msg, from_address, to_address, passwd): server.sendmail(from_address, to_address, msg.as_string()) server.close() except: - print "Couldn't send email." + print("Couldn't send email.") def createPDF(report): @@ -420,6 +419,9 @@ def selectTemplate(format): return 'statement.html.jinja' -if __name__ == '__main__': +def main(): app.debug = True - app.run() \ No newline at end of file + app.run() + +if __name__ == '__main__': + main() \ No newline at end of file From 219c1ada2f3425f935ba7eeee31d99b00e22fdf4 Mon Sep 17 00:00:00 2001 From: JohnKent Date: Tue, 23 Jun 2020 22:00:23 -0400 Subject: [PATCH 19/38] Data file updates. --- 9Kloan.json | 96 ++++++++++++++++++++- brendamortgage.json | 143 ++++++++++++++++++++++++++++++- dadmortgage.json | 143 ++++++++++++++++++++++++++++++- greenfield_mortgage.json | 176 ++++++++++++++++++++++++++++++++++++++- testloan.json | 65 ++++----------- 5 files changed, 569 insertions(+), 54 deletions(-) diff --git a/9Kloan.json b/9Kloan.json index d54e2af..a01c4b4 100644 --- a/9Kloan.json +++ b/9Kloan.json @@ -1 +1,95 @@ -{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 475.0, "interest_rate": 6.5, "start_interest_date": "2019-06-17", "payment_day_of_month": "15", "first_payment_month": "2019-07-15", "account_number": "100002", "periods": 20, "start_date": "2019-06-15", "periods_per_year": 12, "principal": 9000.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "grady@gradystreet.com", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Washington", "name": "Rivanna Graphite Investments, LLC", "zip": "20011", "phone": "703.343.0782", "state": "DC", "address": "743 Madison St NW"}, "header": {"date": "Today", "title": "Installment Loan Statement"}, "payments": [["2019-07-15", "475.00"], ["2019-08-14", "475.00"], ["2019-09-17", "475"], ["2019-10-15", "0"], ["2019-11-15", "0"], ["2019-12-5", "475"]], "borrower": {"city": "Alexandria", "state": "VA", "name": "Bear Houses, LLC", "zip": "22312", "address": "301 N Beauregard St Apt 203"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 475.0, + "interest_rate": 6.5, + "start_interest_date": "2019-06-17", + "payment_day_of_month": "15", + "first_payment_month": "2019-07-15", + "account_number": "100002", + "periods": 20, + "start_date": "2019-06-15", + "periods_per_year": 12, + "principal": 9000.0 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "grady@gradystreet.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "pvyrbcnzrjoizprn", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Washington", + "name": "Rivanna Graphite Investments, LLC", + "zip": "20008", + "phone": "703.343.0782", + "state": "DC", + "address": "3100 Connecticut Ave NW Apt 144" + }, + "header": { + "date": "Today", + "title": "Installment Loan Statement" + }, + "payments": [ + [ + "2019-07-15", + "475.00" + ], + [ + "2019-08-14", + "475.00" + ], + [ + "2019-09-17", + "475" + ], + [ + "2019-10-15", + "0" + ], + [ + "2019-11-15", + "0" + ], + [ + "2019-12-5", + "475" + ], + [ + "2020-01-15", + "0", + "0" + ], + [ + "2020-02-17", + "0" + ], + [ + "2020-03-03", + "475" + ], + [ + "2020-04-15", + "0" + ], + [ + "2020-05-19", + "475" + ] + ], + "borrower": { + "city": "Alexandria", + "state": "VA", + "name": "Bear Houses, LLC", + "zip": "22312", + "address": "301 N Beauregard St Apt 203" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/brendamortgage.json b/brendamortgage.json index 948cec0..12e74ba 100644 --- a/brendamortgage.json +++ b/brendamortgage.json @@ -1 +1,142 @@ -{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 278.15, "interest_rate": 5.0, "start_interest_date": "2018-07-01", "payment_day_of_month": "01", "first_payment_month": "2018-08-01", "account_number": "100001", "periods": 180, "start_date": "2018-06-25", "periods_per_year": 12, "principal": 35000.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "bck@virginia.edu", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Scottsville", "name": "Brenda Kelly", "zip": "24590", "phone": "434-286-2110", "state": "VA", "address": "150 Confederate Street"}, "header": {"date": "Today", "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA"}, "payments": [["2018-08-01", "278.15"], ["2018-09-01", "278.15"], ["2018-10-01", "278.15"], ["2018-11-01", "278.15"], ["2018-12-01", "278.15"], ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], ["2019-03-01", "278.15"], ["2019-04-01", "278.15"], ["2019-05-01", "278.15"], ["2019-05-31", "278.15"], ["2019-07-01", "278.15"], ["2019-08-01", "278.15"], ["2019-09-02", "278.15"], ["2019-10-01", "278.15"], ["2019-11-01", "278.15"], ["2019-12-02", "278.15"], ["2020-01-02", "278.15"]], "borrower": {"city": "Washington", "state": "DC", "name": "Grandma Tina's Properties, LLC", "zip": "20011", "address": "743 Madison St NW"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 278.15, + "interest_rate": 5.0, + "start_interest_date": "2018-07-01", + "payment_day_of_month": "01", + "first_payment_month": "2018-08-01", + "account_number": "100001", + "periods": 180, + "start_date": "2018-06-25", + "periods_per_year": 12, + "principal": 35000.0 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "bck@virginia.edu", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "pvyrbcnzrjoizprn", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Scottsville", + "name": "Brenda Kelly", + "zip": "24590", + "phone": "434-286-2110", + "state": "VA", + "address": "150 Confederate Street" + }, + "header": { + "date": "Today", + "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA" + }, + "payments": [ + [ + "2018-08-01", + "278.15" + ], + [ + "2018-09-01", + "278.15" + ], + [ + "2018-10-01", + "278.15" + ], + [ + "2018-11-01", + "278.15" + ], + [ + "2018-12-01", + "278.15" + ], + [ + "2019-01-01", + "278.15" + ], + [ + "2019-02-01", + "278.15" + ], + [ + "2019-03-01", + "278.15" + ], + [ + "2019-04-01", + "278.15" + ], + [ + "2019-05-01", + "278.15" + ], + [ + "2019-05-31", + "278.15" + ], + [ + "2019-07-01", + "278.15" + ], + [ + "2019-08-01", + "278.15" + ], + [ + "2019-09-02", + "278.15" + ], + [ + "2019-10-01", + "278.15" + ], + [ + "2019-11-01", + "278.15" + ], + [ + "2019-12-02", + "278.15" + ], + [ + "2020-01-02", + "278.15" + ], + [ + "2020-02-01", + "278.15" + ], + [ + "2020-03-01", + "278.15" + ], + [ + "2020-04-01", + "278.15" + ], + [ + "2020-05-01", + "278.15" + ], + [ + "2020-06-01", + "278.15" + ] + ], + "borrower": { + "city": "Washington", + "state": "DC", + "name": "Grandma Tina's Properties, LLC", + "zip": "20008", + "address": "3100 Connecticut Ave NW Apt 144" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/dadmortgage.json b/dadmortgage.json index 933f1ee..919852a 100644 --- a/dadmortgage.json +++ b/dadmortgage.json @@ -1 +1,142 @@ -{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 278.15, "interest_rate": 5.0, "start_interest_date": "2018-07-01", "payment_day_of_month": "01", "first_payment_month": "2018-08-01", "account_number": "100001", "periods": 180, "start_date": "2018-06-25", "periods_per_year": 12, "principal": 35000.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "johnkent49@gmail.com", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Palmyra", "name": "John Kent", "zip": "22963", "phone": "434-510-7272", "state": "VA", "address": "109 Shores Rd"}, "header": {"date": "Today", "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA"}, "payments": [["2018-08-01", "278.15"], ["2018-09-01", "278.15"], ["2018-10-01", "278.15"], ["2018-11-01", "278.15"], ["2018-12-01", "278.15"], ["2019-01-01", "278.15"], ["2019-02-01", "278.15"], ["2019-03-01", "278.15"], ["2019-04-01", "278.15"], ["2019-05-01", "278.15"], ["2019-05-31", "278.15"], ["2019-07-01", "278.15"], ["2019-08-01", "278.15"], ["2019-09-02", "278.15"], ["2019-10-01", "278.15"], ["2019-11-01", "278.15"], ["2019-12-02", "278.15"], ["2020-01-02", "278.15"]], "borrower": {"city": "Washington", "state": "DC", "name": "Grandma Tina's Properties, LLC", "zip": "20011", "address": "743 Madison St NW"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 278.15, + "interest_rate": 5.0, + "start_interest_date": "2018-07-01", + "payment_day_of_month": "01", + "first_payment_month": "2018-08-01", + "account_number": "100001", + "periods": 180, + "start_date": "2018-06-25", + "periods_per_year": 12, + "principal": 35000.0 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "johnkent49@gmail.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "pvyrbcnzrjoizprn", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Palmyra", + "name": "John Kent", + "zip": "22963", + "phone": "434-510-7272", + "state": "VA", + "address": "109 Shores Rd" + }, + "header": { + "date": "Today", + "title": "Mortgage Loan Statement - 185 James River Rd, Scottsville VA" + }, + "payments": [ + [ + "2018-08-01", + "278.15" + ], + [ + "2018-09-01", + "278.15" + ], + [ + "2018-10-01", + "278.15" + ], + [ + "2018-11-01", + "278.15" + ], + [ + "2018-12-01", + "278.15" + ], + [ + "2019-01-01", + "278.15" + ], + [ + "2019-02-01", + "278.15" + ], + [ + "2019-03-01", + "278.15" + ], + [ + "2019-04-01", + "278.15" + ], + [ + "2019-05-01", + "278.15" + ], + [ + "2019-05-31", + "278.15" + ], + [ + "2019-07-01", + "278.15" + ], + [ + "2019-08-01", + "278.15" + ], + [ + "2019-09-02", + "278.15" + ], + [ + "2019-10-01", + "278.15" + ], + [ + "2019-11-01", + "278.15" + ], + [ + "2019-12-02", + "278.15" + ], + [ + "2020-01-02", + "278.15" + ], + [ + "2020-02-01", + "278.15" + ], + [ + "2020-03-02", + "278.15" + ], + [ + "2020-04-01", + "278.15" + ], + [ + "2020-05-01", + "278.15" + ], + [ + "2020-06-01", + "278.15" + ] + ], + "borrower": { + "city": "Washington", + "state": "DC", + "name": "Grandma Tina's Properties, LLC", + "zip": "20008", + "address": "3100 Connecticut Ave NW Apt 144" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/greenfield_mortgage.json b/greenfield_mortgage.json index e278296..1ea4eee 100644 --- a/greenfield_mortgage.json +++ b/greenfield_mortgage.json @@ -1 +1,175 @@ -{"htmlTemplate": "statement.pdf.jinja", "parameters": {"monthly_payment": 803.0, "interest_rate": 5.5, "start_interest_date": "2017-11-11", "payment_day_of_month": "15", "first_payment_month": "2017-12-15", "account_number": "100001", "periods": 185, "start_date": "2017-11-07", "periods_per_year": 12, "principal": 97750.0}, "format": "html", "email": {"body": "Your loan statement is attached.", "to_address": "grady@gradystreet.com", "from_address": "jkent3rd@gmail.com", "text": "Please see your most recent account statement.", "server": "smtp.gmail.com", "send_text": "true", "template": "./template.txt", "send_pdf": "true", "password": "pvyrbcnzrjoizprn", "subject": "Your loan statement..."}, "lender": {"city": "Washington", "name": "Rivanna Graphite Investments, LLC", "zip": "20011", "phone": "703.343.0782", "state": "DC", "address": "743 Madison St NW"}, "header": {"date": "Today", "title": "Mortgage Loan Statement - 195 Greenfield Lane, Pearl MS"}, "payments": [["2017-12-11", "803.00"], ["2018-01-23", "803.00"], ["2018-03-23", "803.00"], ["2018-04-18", "803.00"], ["2018-04-26", "803.00"], ["2018-05-15", "0.00"], ["2018-06-15", "0.00"], ["2018-07-12", "803.00"], ["2018-08-07", "803.00"], ["2018-09-06", "803.00"], ["2018-10-11", "803.00"], ["2018-11-13", "803.00"], ["2018-12-13", "803.00"], ["2019-01-14", "803.00"], ["2019-02-05", "803.00"], ["2019-03-05", "803.00"], ["2019-04-15", "803.00"], ["2019-05-15", "0.00"], ["2019-06-13", "803.00"], ["2019-07-25", "803.00"], ["2019-08-14", "803.00"], ["2019-09-13", "803.00"], ["2019-10-21", "803.00"], ["2019-11-29", "803.00"], ["2019-12-19", "803.00"], ["2020-01-15", "803"]], "borrower": {"city": "Alexandria", "state": "VA", "name": "Bear Houses, LLC", "zip": "22312", "address": "301 N Beauregard St Apt 203"}, "txtTemplate": "statement.txt.jinja"} \ No newline at end of file +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 803.0, + "interest_rate": 5.5, + "start_interest_date": "2017-11-11", + "payment_day_of_month": "15", + "first_payment_month": "2017-12-15", + "account_number": "100001", + "periods": 185, + "start_date": "2017-11-07", + "periods_per_year": 12, + "principal": 97750.0 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "grady@gradystreet.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "pvyrbcnzrjoizprn", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Washington", + "name": "Rivanna Graphite Investments, LLC", + "zip": "20008", + "phone": "703.343.0782", + "state": "DC", + "address": "3100 Connecticut Ave NW Apt 144" + }, + "header": { + "date": "Today", + "title": "Mortgage Loan Statement - 195 Greenfield Lane, Pearl MS" + }, + "payments": [ + [ + "2017-12-11", + "803.00" + ], + [ + "2018-01-23", + "803.00" + ], + [ + "2018-03-23", + "803.00" + ], + [ + "2018-04-18", + "803.00" + ], + [ + "2018-04-26", + "803.00" + ], + [ + "2018-05-15", + "0.00" + ], + [ + "2018-06-15", + "0.00" + ], + [ + "2018-07-12", + "803.00" + ], + [ + "2018-08-07", + "803.00" + ], + [ + "2018-09-06", + "803.00" + ], + [ + "2018-10-11", + "803.00" + ], + [ + "2018-11-13", + "803.00" + ], + [ + "2018-12-13", + "803.00" + ], + [ + "2019-01-14", + "803.00" + ], + [ + "2019-02-05", + "803.00" + ], + [ + "2019-03-05", + "803.00" + ], + [ + "2019-04-15", + "803.00" + ], + [ + "2019-05-15", + "0.00" + ], + [ + "2019-06-13", + "803.00" + ], + [ + "2019-07-25", + "803.00" + ], + [ + "2019-08-14", + "803.00" + ], + [ + "2019-09-13", + "803.00" + ], + [ + "2019-10-21", + "803.00" + ], + [ + "2019-11-29", + "803.00" + ], + [ + "2019-12-19", + "803.00" + ], + [ + "2020-01-15", + "803" + ], + [ + "2020-03-06", + "803", + "32.12" + ], + [ + "2020-03-13", + "803.00" + ], + [ + "2020-04-15", + "803.00" + ], + [ + "2020-05-13", + "803.00" + ], + [ + "2020-06-16", + "803.00" + ] + ], + "borrower": { + "city": "Alexandria", + "state": "VA", + "name": "Bear Houses, LLC", + "zip": "22312", + "address": "301 N Beauregard St Apt 203" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/testloan.json b/testloan.json index 336c69c..0be860b 100644 --- a/testloan.json +++ b/testloan.json @@ -23,70 +23,35 @@ "date": "Today", "title": "Installment Loan Test Statement" }, - "payments": [ + "payments": [ [ - "2018-01-16", - "589.00" + "2019-07-15", + "475.00" ], [ - "2018-03-30", - "589.00" + "2019-08-14", + "475.00" ], [ - "2018-04-06", - "589.00" + "2019-09-17", + "475" ], [ - "2018-04-12", - "589.00" - ], - [ - "2018-05-28", - "589.00" - ], - [ - "2018-06-12", - "589.00" - ], - [ - "2018-07-13", - "589.00" - ], - [ - "2018-08-14", - "589.00" - ], - [ - "2018-09-26", - "589.00" - ], - [ - "2018-10-15", + "2019-10-15", "0" ], [ - "2018-11-29", - "589.00" + "2019-11-15", + "0" ], [ - "2018-12-31", - "589.00" + "2019-12-5", + "475" ], [ - "2019-01-28", - "589.00" - ], - [ - "2019-03-15", - "589.00" - ], - [ - "2019-07-10", - "123.12" - ], - [ - "2019-07-10", - "120" + "2020-01-15", + "0", + "50" ] ], "borrower": { From e5830536a134ef66f9cfb3f1caf3984a105e6cde Mon Sep 17 00:00:00 2001 From: JohnKent Date: Tue, 23 Jun 2020 22:03:38 -0400 Subject: [PATCH 20/38] Added late fee logic to calculation logic and the templates. user: JohnKent branch 'default' added .hgignore changed mortgage/templates/main.html changed mortgage/templates/statement.html.jinja changed mortgage/templates/statement.pdf.jinja changed mortgage/templates/statement.text.jinja changed mortgage/web.py --- .hgignore | 3 +++ mortgage/templates/main.html | 18 +++++++++++------- mortgage/templates/statement.html.jinja | 10 ++++++---- mortgage/templates/statement.pdf.jinja | 14 ++++++++------ mortgage/templates/statement.text.jinja | 2 ++ mortgage/web.py | 15 ++++++++++----- 6 files changed, 40 insertions(+), 22 deletions(-) create mode 100644 .hgignore diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..1545b02 --- /dev/null +++ b/.hgignore @@ -0,0 +1,3 @@ +./venv/ +.hgignore +.Python diff --git a/mortgage/templates/main.html b/mortgage/templates/main.html index 9edc972..1b0c7db 100644 --- a/mortgage/templates/main.html +++ b/mortgage/templates/main.html @@ -74,17 +74,18 @@ - + - - - - + + + + + @@ -97,13 +98,14 @@ + {% if item.month == 12 or loop.last %} - + {% endif %} {% endfor %} - +
    Loan HistoryLoan History
    # Due Date Date Paid Days InterestPayment AmtPrincipal PmtInterest PmtNew BalancePayment AmtPrincipal PmtInterest PmtLate FeeNew Balance
    {{ "$%.2f"|format(item.payment_amount) }} {{ "$%.2f"|format(item.principal_payment) }} {{ "$%.2f"|format(item.interest_payment) }} {{ "$%.2f"|format(item.late_fee) }} {{ "$%.2f"|format(item.new_balance) }}
    Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.
    Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.
    Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.
    Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.
@@ -172,6 +174,8 @@ Payment Amount: + Late Fee Amount: + diff --git a/mortgage/templates/statement.html.jinja b/mortgage/templates/statement.html.jinja index 48e124c..72db61f 100644 --- a/mortgage/templates/statement.html.jinja +++ b/mortgage/templates/statement.html.jinja @@ -27,10 +27,11 @@ Due Date Date Paid Days Interest - Payment Amt - Principal Pmt - Interest Pmt - New Balance + Payment Amt + Principal Pmt + Interest Pmt + Late Fee + New Balance {% for item in model.past_payments %} @@ -41,6 +42,7 @@ {{ "$%.2f"|format(item.payment_amount) }} {{ "$%.2f"|format(item.principal_payment) }} {{ "$%.2f"|format(item.interest_payment) }} + {{ "$%.2f"|format(item.late_fee) }} {{ "$%.2f"|format(item.new_balance) }} {% if item.month == 12 or loop.last %} diff --git a/mortgage/templates/statement.pdf.jinja b/mortgage/templates/statement.pdf.jinja index 48e124c..944d6f3 100644 --- a/mortgage/templates/statement.pdf.jinja +++ b/mortgage/templates/statement.pdf.jinja @@ -27,10 +27,11 @@ Due Date Date Paid Days Interest - Payment Amt - Principal Pmt - Interest Pmt - New Balance + Payment Amt + Principal Pmt + Interest Pmt + Late Fee + New Balance {% for item in model.past_payments %} @@ -41,6 +42,7 @@ {{ "$%.2f"|format(item.payment_amount) }} {{ "$%.2f"|format(item.principal_payment) }} {{ "$%.2f"|format(item.interest_payment) }} + {{ "$%.2f"|format(item.late_fee) }} {{ "$%.2f"|format(item.new_balance) }} {% if item.month == 12 or loop.last %} @@ -56,9 +58,9 @@ - + - + diff --git a/mortgage/templates/statement.text.jinja b/mortgage/templates/statement.text.jinja index d7fbef2..e205e2b 100644 --- a/mortgage/templates/statement.text.jinja +++ b/mortgage/templates/statement.text.jinja @@ -7,6 +7,7 @@ Payment History Payment Amount Principal Payment Interest Payment + Late Fee New Balance {% for item in past_payments %} @@ -17,6 +18,7 @@ Payment History {{ item.payment_amount }} {{ item.principal_payment }} {{ item.interest_payment }} +{{ item.late_fee }} {{ item.new_balance }} {% if item.print_interest_total %} diff --git a/mortgage/web.py b/mortgage/web.py index 90da3df..7413512 100644 --- a/mortgage/web.py +++ b/mortgage/web.py @@ -74,9 +74,9 @@ def update_file(): if proceed_flag is True: try: - backup_filename = loanFile + ".backup-" + datetime.now().strftime("%Y-%m-%d %H-%M-%S") + backup_filename = loanFile + ".backup-" + datetime.now().strftime("%Y-%m-%d %H-%M-%S") + ".json" backup_file = open(getFullPathofLoanFile(backup_filename), 'w+') - json.dump(data, backup_file) + json.dump(data, backup_file, indent=2) backup_file.close() except: messages.append("A backup file could not be created. Your payment was not recorded.") @@ -88,7 +88,7 @@ def update_file(): try: payment_history.append( [payment_date, str(payment_amount)]) file = open(getFullPathofLoanFile(loanFile), 'w+') - json.dump(data, file) + json.dump(data, file, indent=2) file.close() except: messages.append("An error occurred writing to the file. Your payment file may be corrupt, " + \ @@ -252,6 +252,10 @@ def amortizeLoan(loan): 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 + if len(payment)>2: + late_fee = Decimal(payment[2]).quantize(Decimal("1.00")) + else: + late_fee = Decimal("0.00") #check for out of order payments, generally a sign of a data entry problem, especially years if days_since_last_payment < 0: @@ -260,7 +264,7 @@ def amortizeLoan(loan): quit() new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) - new_principal = payment_amount - new_interest + new_principal = payment_amount - new_interest - late_fee interest_paid_through_date = payment_date total_interest = total_interest + new_interest annual_interest = annual_interest + new_interest @@ -276,11 +280,12 @@ def amortizeLoan(loan): 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['principal_payment'] = payment_amount - new_interest - late_fee 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 + payment_record['late_fee'] = late_fee past_payments.append(payment_record) payment_number = payment_number + 1 From 3ccd81cf76598d3fcdf76427df9e70a91a1104bb Mon Sep 17 00:00:00 2001 From: john Date: Sat, 16 Jan 2021 14:19:14 -0500 Subject: [PATCH 21/38] Added a requirements.txt to support dockerfile deployment. --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..57e31fa --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +fpdf +jinja2 \ No newline at end of file From 466d2dce8b68288440a13779da7b58daa0bbedeb Mon Sep 17 00:00:00 2001 From: john Date: Mon, 3 Oct 2022 00:02:12 -0400 Subject: [PATCH 22/38] :G: Enter commit message. Lines beginning with 'HG:' are removed. --- .hgignore | 6 +- testloan.json => 0_Test_Loan.loan | 34 +- 10Kloan.json | 69 --- dadmortgage.json => 1_Dad_Mortgage.loan | 156 ++++- brendamortgage.json => 2_Brenda_Mortgage.loan | 156 ++++- 9Kloan.json => 3_839_Harbor_Bend_2022.loan | 71 +-- ... => _Archive_Greenfield_Lane_Mortgage.loan | 86 ++- _Archive_Greenfield_Lane_Refinance-1.loan | 128 +++++ _Archive_Old_Harbor_Bend_Mortgage.loan | 50 ++ _Archive_Wayneland_Mortgage.loan | 137 +++++ mortgage.py | 130 ----- mortgage/calculate_interest_heloc.py | 68 +++ mortgage/mortgage_template.py | 345 ----------- mortgage/templates/main.html | 20 +- .../payment_received_email.html.jinja | 23 + mortgage/templates/statement.text.jinja | 75 +-- mortgage/web.py | 254 +++++--- mortgage/web_ng.py | 544 ++++++++++++++++++ pyvenv.cfg | 3 + requirements.txt | 4 +- 20 files changed, 1630 insertions(+), 729 deletions(-) rename testloan.json => 0_Test_Loan.loan (76%) delete mode 100644 10Kloan.json rename dadmortgage.json => 1_Dad_Mortgage.loan (54%) rename brendamortgage.json => 2_Brenda_Mortgage.loan (54%) rename 9Kloan.json => 3_839_Harbor_Bend_2022.loan (52%) rename greenfield_mortgage.json => _Archive_Greenfield_Lane_Mortgage.loan (72%) create mode 100644 _Archive_Greenfield_Lane_Refinance-1.loan create mode 100644 _Archive_Old_Harbor_Bend_Mortgage.loan create mode 100644 _Archive_Wayneland_Mortgage.loan delete mode 100644 mortgage.py create mode 100644 mortgage/calculate_interest_heloc.py delete mode 100644 mortgage/mortgage_template.py create mode 100644 mortgage/templates/payment_received_email.html.jinja create mode 100644 mortgage/web_ng.py create mode 100644 pyvenv.cfg diff --git a/.hgignore b/.hgignore index 1545b02..40b7757 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,5 @@ -./venv/ -.hgignore .Python +.DS_Store +./bin/ +./lib/ +./.idea/ diff --git a/testloan.json b/0_Test_Loan.loan similarity index 76% rename from testloan.json rename to 0_Test_Loan.loan index 0be860b..e8faba3 100644 --- a/testloan.json +++ b/0_Test_Loan.loan @@ -23,7 +23,7 @@ "date": "Today", "title": "Installment Loan Test Statement" }, - "payments": [ + "payments": [ [ "2019-07-15", "475.00" @@ -52,6 +52,36 @@ "2020-01-15", "0", "50" + ], + [ + "2020-09-27", + "278.15", + "20" + ], + [ + "2020-09-29", + "296.12", + "25" + ], + [ + "2020-10-16", + "133", + "0.00" + ], + [ + "2020-10-16", + "133", + "0.00" + ], + [ + "2020-10-16", + "133", + "0.00" + ], + [ + "2021-05-03", + "1", + "1" ] ], "borrower": { @@ -67,6 +97,6 @@ "subject": "Your test loan statement...", "body": "Your test loan statement is attached.", "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn" + "password": "builcuouzobxroow" } } \ No newline at end of file diff --git a/10Kloan.json b/10Kloan.json deleted file mode 100644 index 3bac0b5..0000000 --- a/10Kloan.json +++ /dev/null @@ -1,69 +0,0 @@ -{ -"htmlTemplate": "statement.pdf.jinja", -"txtTemplate": "statement.txt.jinja", -"format": "html", -"email": { - "from_address": "jkent3rd@gmail.com", - "to_address": "grady@gradystreet.com", - "server": "smtp.gmail.com", - "password": "pvyrbcnzrjoizprn", - "template": "./template.txt", - "send_pdf": "true", - "send_text": "true", - "subject": "Your loan statement...", - "body": "Your loan statement is attached.", - "text": "Please see your most recent account statement." - }, -"parameters": { - "account_number": "100100", - "principal": 10020.00, - "interest_rate": 6.5, - "periods_per_year": 12, - "periods": 18, - "start_date": "2017-12-05", - "start_interest_date": "2017-12-05", - "first_payment_month": "2018-01-15", - "monthly_payment": 589.00, - "payment_day_of_month": "15" - }, -"payments": [ - ["2018-01-16", "589.00"], - ["2018-03-30", "589.00"], - ["2018-04-06", "589.00"], - ["2018-04-12", "589.00"], - ["2018-05-28", "589.00"], - ["2018-06-12", "589.00"], - ["2018-07-13", "589.00"], - ["2018-08-14", "589.00"], - ["2018-09-26", "589.00"], - ["2018-10-15", "0"], - ["2018-11-29", "589.00"], - ["2018-12-31", "589.00"], - ["2019-01-15", "0.00"], - ["2019-02-13", "589.00"], - ["2019-03-15", "0"], - ["2019-04-15", "0"], - ["2019-05-15", "589.00"], - ["2019-06-21", "2985.00"] -], -"borrower": { - "name": "Bear Houses, LLC", - "address": "301 N Beauregard St Apt 203", - "city": "Alexandria", - "state": "VA", - "zip": "22312" - }, -"lender": { - "name": "John Kent", - "phone": "703.343.0782", - "address": "743 Madison St NW", - "city": "Washington", - "state": "DC", - "zip": "20011" - }, -"header": { - "title": "Installment Loan Statement", - "date": "Today" - } -} - diff --git a/dadmortgage.json b/1_Dad_Mortgage.loan similarity index 54% rename from dadmortgage.json rename to 1_Dad_Mortgage.loan index 919852a..c0faec1 100644 --- a/dadmortgage.json +++ b/1_Dad_Mortgage.loan @@ -22,7 +22,7 @@ "send_text": "true", "template": "./template.txt", "send_pdf": "true", - "password": "pvyrbcnzrjoizprn", + "password": "builcuouzobxroow", "subject": "Your loan statement..." }, "lender": { @@ -129,14 +129,164 @@ [ "2020-06-01", "278.15" + ], + [ + "2020-07-01", + "278.15" + ], + [ + "2020-08-01", + "278.15" + ], + [ + "2020-09-01", + "278.15" + ], + [ + "2020-10-01", + "278.15" + ], + [ + "2020-11-01", + "278.15", + "0.00" + ], + [ + "2020-12-01", + "278.15", + "0.00" + ], + [ + "2021-01-01", + "278.15", + "0" + ], + [ + "2021-02-01", + "278.15", + "0" + ], + [ + "2021-03-01", + "278.15", + "0.00" + ], + [ + "2021-04-01", + "278.15", + "0.00" + ], + [ + "2021-04-30", + "278.15", + "0" + ], + [ + "2021-06-01", + "278.15", + "0.00" + ], + [ + "2021-07-01", + "278.15", + "0" + ], + [ + "2021-07-31", + "278.15", + "0" + ], + [ + "2021-09-01", + "278.15", + "0.00", + "False" + ], + [ + "2021-10-01", + "278.15", + "0.00", + "False" + ], + [ + "2021-11-01", + "278.15", + "0", + "False" + ], + [ + "2021-12-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-01-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-02-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-03-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-04-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-05-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-06-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-07-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-08-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-09-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-10-01", + "278.15", + "0.00", + "False" ] ], "borrower": { "city": "Washington", "state": "DC", "name": "Grandma Tina's Properties, LLC", - "zip": "20008", - "address": "3100 Connecticut Ave NW Apt 144" + "zip": "20001", + "address": "1720 New Jersey Ave NW Unit 401" }, "txtTemplate": "statement.txt.jinja" } \ No newline at end of file diff --git a/brendamortgage.json b/2_Brenda_Mortgage.loan similarity index 54% rename from brendamortgage.json rename to 2_Brenda_Mortgage.loan index 12e74ba..848a549 100644 --- a/brendamortgage.json +++ b/2_Brenda_Mortgage.loan @@ -22,7 +22,7 @@ "send_text": "true", "template": "./template.txt", "send_pdf": "true", - "password": "pvyrbcnzrjoizprn", + "password": "builcuouzobxroow", "subject": "Your loan statement..." }, "lender": { @@ -129,14 +129,164 @@ [ "2020-06-01", "278.15" + ], + [ + "2020-07-01", + "278.15" + ], + [ + "2020-08-01", + "278.15" + ], + [ + "2020-09-01", + "278.15" + ], + [ + "2020-10-01", + "278.15" + ], + [ + "2020-11-01", + "278.15", + "0.00" + ], + [ + "2020-12-01", + "278.15", + "0.00" + ], + [ + "2021-01-01", + "278.15", + "0" + ], + [ + "2021-02-01", + "278.15", + "0" + ], + [ + "2021-03-01", + "278.15", + "0.00" + ], + [ + "2021-04-01", + "278.15", + "0.00" + ], + [ + "2021-04-30", + "278.15", + "0" + ], + [ + "2021-06-01", + "278.15", + "0.00" + ], + [ + "2021-07-01", + "278.15", + "0" + ], + [ + "2021-07-31", + "278.15", + "0" + ], + [ + "2021-09-01", + "278.15", + "0.00", + "False" + ], + [ + "2021-10-01", + "278.15", + "0.00", + "False" + ], + [ + "2021-11-01", + "278.15", + "0", + "False" + ], + [ + "2021-12-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-01-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-02-01", + "278.15", + "0", + "False" + ], + [ + "2022-03-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-04-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-05-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-06-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-07-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-08-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-09-01", + "278.15", + "0.00", + "False" + ], + [ + "2022-10-01", + "278.15", + "0.00", + "False" ] ], "borrower": { "city": "Washington", "state": "DC", "name": "Grandma Tina's Properties, LLC", - "zip": "20008", - "address": "3100 Connecticut Ave NW Apt 144" + "zip": "20001", + "address": "1720 New Jersey Ave NW Unit 401" }, "txtTemplate": "statement.txt.jinja" } \ No newline at end of file diff --git a/9Kloan.json b/3_839_Harbor_Bend_2022.loan similarity index 52% rename from 9Kloan.json rename to 3_839_Harbor_Bend_2022.loan index a01c4b4..3d41c1f 100644 --- a/9Kloan.json +++ b/3_839_Harbor_Bend_2022.loan @@ -1,16 +1,16 @@ { "htmlTemplate": "statement.pdf.jinja", "parameters": { - "monthly_payment": 475.0, - "interest_rate": 6.5, - "start_interest_date": "2019-06-17", + "monthly_payment": 842.93, + "interest_rate": 5.75, + "start_interest_date": "2022-08-12", "payment_day_of_month": "15", - "first_payment_month": "2019-07-15", - "account_number": "100002", - "periods": 20, - "start_date": "2019-06-15", + "first_payment_month": "2022-09-15", + "account_number": "22-0001", + "periods": 180, + "start_date": "2022-08-12", "periods_per_year": 12, - "principal": 9000.0 + "principal": 101508.04 }, "format": "html", "email": { @@ -22,66 +22,33 @@ "send_text": "true", "template": "./template.txt", "send_pdf": "true", - "password": "pvyrbcnzrjoizprn", + "password": "builcuouzobxroow", "subject": "Your loan statement..." }, "lender": { "city": "Washington", "name": "Rivanna Graphite Investments, LLC", - "zip": "20008", + "zip": "20001", "phone": "703.343.0782", "state": "DC", - "address": "3100 Connecticut Ave NW Apt 144" + "address": "1720 New Jersey Ave NW Unit 401" }, "header": { "date": "Today", - "title": "Installment Loan Statement" + "title": "Mortgage Loan Statement - Harbor Bend Loan 1" }, "payments": [ [ - "2019-07-15", - "475.00" - ], - [ - "2019-08-14", - "475.00" - ], - [ - "2019-09-17", - "475" - ], - [ - "2019-10-15", - "0" - ], - [ - "2019-11-15", - "0" - ], - [ - "2019-12-5", - "475" - ], - [ - "2020-01-15", + "2022-08-15", + "803", "0", - "0" + "False" ], [ - "2020-02-17", - "0" - ], - [ - "2020-03-03", - "475" - ], - [ - "2020-04-15", - "0" - ], - [ - "2020-05-19", - "475" + "2022-09-09", + "803", + "0.00", + "False" ] ], "borrower": { diff --git a/greenfield_mortgage.json b/_Archive_Greenfield_Lane_Mortgage.loan similarity index 72% rename from greenfield_mortgage.json rename to _Archive_Greenfield_Lane_Mortgage.loan index 1ea4eee..22fe53d 100644 --- a/greenfield_mortgage.json +++ b/_Archive_Greenfield_Lane_Mortgage.loan @@ -22,16 +22,16 @@ "send_text": "true", "template": "./template.txt", "send_pdf": "true", - "password": "pvyrbcnzrjoizprn", + "password": "builcuouzobxroow", "subject": "Your loan statement..." }, "lender": { "city": "Washington", "name": "Rivanna Graphite Investments, LLC", - "zip": "20008", + "zip": "20001", "phone": "703.343.0782", "state": "DC", - "address": "3100 Connecticut Ave NW Apt 144" + "address": "1720 New Jersey Ave NW Unit 401" }, "header": { "date": "Today", @@ -162,6 +162,86 @@ [ "2020-06-16", "803.00" + ], + [ + "2020-07-15", + "803" + ], + [ + "2020-08-13", + "803" + ], + [ + "2020-09-17", + "803" + ], + [ + "2020-10-15", + "803", + "0.00" + ], + [ + "2020-11-16", + "803.00", + "0" + ], + [ + "2020-12-15", + "803.00", + "0.00" + ], + [ + "2021-01-13", + "803.00", + "0" + ], + [ + "2021-02-16", + "803.00", + "0" + ], + [ + "2021-03-22", + "0", + "32.12" + ], + [ + "2021-04-14", + "803.00", + "0.00" + ], + [ + "2021-05-18", + "803.00", + "0" + ], + [ + "2021-06-15", + "803.00", + "0" + ], + [ + "2021-07-31", + "0", + "32.12" + ], + [ + "2021-08-18", + "803", + "0.00", + "False" + ], + [ + "2021-09-24", + "803", + "0.00", + "False" + ], + [ + "2021-09-25", + "0", + "0.00", + "False" ] ], "borrower": { diff --git a/_Archive_Greenfield_Lane_Refinance-1.loan b/_Archive_Greenfield_Lane_Refinance-1.loan new file mode 100644 index 0000000..5ab8f08 --- /dev/null +++ b/_Archive_Greenfield_Lane_Refinance-1.loan @@ -0,0 +1,128 @@ +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 831.0, + "interest_rate": 5.5, + "start_interest_date": "2021-10-13", + "payment_day_of_month": "15", + "first_payment_month": "2021-11-15", + "account_number": "21-0004", + "periods": 180, + "start_date": "2021-10-13", + "periods_per_year": 12, + "principal": 101123.26 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "grady@gradystreet.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "builcuouzobxroow", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Washington", + "name": "Rivanna Graphite Investments, LLC", + "zip": "20001", + "phone": "703.343.0782", + "state": "DC", + "address": "1720 New Jersey Ave NW Unit 401" + }, + "header": { + "date": "Today", + "title": "Mortgage Loan Statement - 195 Greenfield Lane, Pearl MS" + }, + "payments": [ + [ + "2021-10-14", + "803", + "0.00", + "False" + ], + [ + "2021-10-14", + "28", + "0.00", + "False" + ], + [ + "2021-10-18", + "28", + "0.00", + "False" + ], + [ + "2021-11-16", + "803.00", + "0.00", + "False" + ], + [ + "2021-11-19", + "28", + "0.00", + "False" + ], + [ + "2022-02-10", + "831", + "0.00", + "False" + ], + [ + "2022-04-04", + "1000", + "55.00", + "False" + ], + [ + "2022-05-15", + "0", + "0.00", + "False" + ], + [ + "2022-06-15", + "0", + "0.00", + "False" + ], + [ + "2022-07-15", + "0", + "0.00", + "False" + ], + [ + "2022-07-26", + "803", + "0.00", + "False" + ], + [ + "2022-08-12", + "0", + "0.00", + "False" + ], + [ + "2022-08-12", + "101508.04", + "0.00", + "False" + ] + ], + "borrower": { + "city": "Alexandria", + "state": "VA", + "name": "Bear Houses, LLC", + "zip": "22312", + "address": "301 N Beauregard St Apt 203" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/_Archive_Old_Harbor_Bend_Mortgage.loan b/_Archive_Old_Harbor_Bend_Mortgage.loan new file mode 100644 index 0000000..c89078d --- /dev/null +++ b/_Archive_Old_Harbor_Bend_Mortgage.loan @@ -0,0 +1,50 @@ +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 848, + "interest_rate": 5.75, + "start_interest_date": "2022-08-12", + "payment_day_of_month": "15", + "first_payment_month": "2022-09-15", + "account_number": "22-0001", + "periods": 180, + "start_date": "2022-08-12", + "periods_per_year": 12, + "principal": 101508.04 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "grady@gradystreet.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "builcuouzobxroow", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Washington", + "name": "Rivanna Graphite Investments, LLC", + "zip": "20001", + "phone": "703.343.0782", + "state": "DC", + "address": "1720 New Jersey Ave NW Unit 401" + }, + "header": { + "date": "Today", + "title": "Mortgage Loan Statement - Harbor Bend Loan 1" + }, + "payments": [ + ], + "borrower": { + "city": "Alexandria", + "state": "VA", + "name": "Bear Houses, LLC", + "zip": "22312", + "address": "301 N Beauregard St Apt 203" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/_Archive_Wayneland_Mortgage.loan b/_Archive_Wayneland_Mortgage.loan new file mode 100644 index 0000000..f7cde9e --- /dev/null +++ b/_Archive_Wayneland_Mortgage.loan @@ -0,0 +1,137 @@ +{ + "htmlTemplate": "statement.pdf.jinja", + "parameters": { + "monthly_payment": 175.55, + "interest_rate": 4.5, + "start_interest_date": "2020-07-15", + "payment_day_of_month": "15", + "first_payment_month": "2020-08-15", + "account_number": "100003", + "periods": 60, + "start_date": "2020-07-15", + "periods_per_year": 12, + "principal": 16884 + }, + "format": "html", + "email": { + "body": "Your loan statement is attached.", + "to_address": "grady@gradystreet.com", + "from_address": "jkent3rd@gmail.com", + "text": "Please see your most recent account statement.", + "server": "smtp.gmail.com", + "send_text": "true", + "template": "./template.txt", + "send_pdf": "true", + "password": "builcuouzobxroow", + "subject": "Your loan statement..." + }, + "lender": { + "city": "Washington", + "name": "Rivanna Graphite Investments, LLC", + "zip": "20008", + "phone": "703.343.0782", + "state": "DC", + "address": "3100 Connecticut Ave NW Apt 144" + }, + "header": { + "date": "Today", + "title": "Mortgage Loan Statement - 5025 Wayneland Drive Apt D11 Jackson MS 39211" + }, + "payments": [ + [ + "2020-08-27", + "175.55" + ], + [ + "2020-09-30", + "175.55" + ], + [ + "2020-10-19", + "175.55", + "0.00" + ], + [ + "2020-12-01", + "0", + "17.55" + ], + [ + "2020-12-29", + "0", + "17.55" + ], + [ + "2021-01-20", + "175.55", + "0" + ], + [ + "2021-02-15", + "0.00", + "17.55" + ], + [ + "2021-03-12", + "175.55", + "0.00" + ], + [ + "2021-04-15", + "0", + "17.55" + ], + [ + "2021-05-17", + "0", + "17.55" + ], + [ + "2021-06-30", + "0", + "0.00" + ], + [ + "2021-07-29", + "175.55", + "17.55" + ], + [ + "2021-08-09", + "175.55", + "0" + ], + [ + "2021-08-10", + "175.55", + "0", + "extra" + ], + [ + "2021-09-03", + "175.55", + "0.00", + "False" + ], + [ + "2021-09-17", + "175.55", + "0.00", + "False" + ], + [ + "2021-09-18", + "0", + "0.00", + "False" + ] + ], + "borrower": { + "city": "Alexandria", + "state": "VA", + "name": "Bear Houses, LLC", + "zip": "22312", + "address": "301 N Beauregard St Apt 203" + }, + "txtTemplate": "statement.txt.jinja" +} \ No newline at end of file diff --git a/mortgage.py b/mortgage.py deleted file mode 100644 index 18a582e..0000000 --- a/mortgage.py +++ /dev/null @@ -1,130 +0,0 @@ -import json -from decimal import * -from datetime import * - -# 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. - -#read in the file -#filename = "./10Kloan.txt" -filename = "./dadmortgage.txt" -#filename = "./brendamortgage.txt" -#filename = "./greenfield_mortgage.txt" - -if filename: - with open(filename, 'r') as f: - datastore = json.load(f) - -#read in the loan profile information -annual_rate = Decimal(datastore['interest rate'])/100 -daily_interest_rate = annual_rate/360 -principal = Decimal(datastore["principal"]).quantize(Decimal("1.00")) -periods_per_year = Decimal(datastore["periods per year"]) -total_periods = Decimal(datastore["periods"]) -payment_day_of_month = int(datastore['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")) - - - -print "Principal: ", principal -print "Interest Rate: ", annual_rate -print "Payments Per Year: ", periods_per_year -print "Loan Term: ", total_periods -print "Monthly Payment", monthly_payment -print "Payments due day: ", payment_day_of_month - -print - -#loop over the payments and calculate the actual amortization -actual_payments = datastore["payments"] - -remaining_principal = principal -annual_interest = 0 -total_interest = 0 - -interest_paid_through_date = datetime.strptime(datastore["start_interest_date"], "%Y-%m-%d").date() -next_payment_date = datetime.strptime(datastore["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 - -print "Payment History:" -print "\tBill\t\t\tPayment\tDays of\tPayment\t\tRemaining\t\tPrincipal\tInterest\t\t\tNew" -print "#\tDate\t\t\tDate\tInterst\tAmount\t\tPrincipal\t\tPayment\t\tPayment\t\t\tBalance" -for payment in actual_payments: - payment_date = datetime.strptime((payment[0]), '%Y-%m-%d').date() - payment_amount = Decimal(payment[1]).quantize(Decimal("1.00")) - #print next_bill_date, "\t", next_payment_date, "\t", - days_since_last_payment = (payment_date-interest_paid_through_date).days - #print days_since_last_payment - new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) -# new_principal = monthly_payment - new_interest - new_principal = payment_amount - new_interest - - print payment_number, " ", next_bill_date, "\t\t", payment_date, "\t", days_since_last_payment, "\t", payment_amount, "\t\t\t", remaining_principal, "\t\t", new_principal,\ - "\t\t", 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 - print "\t\t\t", remaining_principal - 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) - print "Total interest paid to date for", old_bill_date.year, " is", annual_interest - else: - next_bill_date = date(year=old_bill_date.year+1, month = 1, day=payment_day_of_month) - print "Total interest for ", old_bill_date.year, " was ", annual_interest - annual_interest = 0 - -print "Total interest paid to date on this loan is", total_interest - -#loop over remaining scheduled payments and present estimated amortization -print "\nEstimated future amortization" - -while (payment_number <= total_periods) and (remaining_principal > 0): - print payment_number, "\t", - payment_number = payment_number+1 - - print next_bill_date, - - days_since_last_payment = (next_bill_date-interest_paid_through_date).days - print days_since_last_payment, "\t", - 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 - - print monthly_payment, "\t\t\t", remaining_principal, "\t\t", new_principal,\ - "\t\t", new_interest, - - remaining_principal = remaining_principal - new_principal - print "\t", remaining_principal - - interest_paid_through_date = next_bill_date - 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) - -if remaining_principal > 0: - print 'Balloon payment due:', remaining_principal diff --git a/mortgage/calculate_interest_heloc.py b/mortgage/calculate_interest_heloc.py new file mode 100644 index 0000000..70e3961 --- /dev/null +++ b/mortgage/calculate_interest_heloc.py @@ -0,0 +1,68 @@ +'''''''take in the csv file of transactions''' +import datetime +from decimal import * +import csv +from calendar import monthrange + +def main(): + getcontext().prec=10 + open_balance = Decimal("-24521.81") + interest_rate = Decimal("8.00")/Decimal("100.00")/Decimal("365.00") + year = 2021 + + start_date = datetime.date(year, 1, 1) + end_date = datetime.date(year, 12, 31)+datetime.timedelta(1) + days_in_year = (end_date - start_date).days + print ("Days in year: %d" % days_in_year) + + balances = [Decimal('0.00')] * days_in_year + interest_invoices = [Decimal('0.00')] * 12 + + with open("/Users/john/Downloads/query_result.csv") as f: + reader = csv.DictReader(f) + for row in reader: + print(row) + year = row['year'] + month = row['month'] + day = row['day'] + amount = row['position (USD)'] + + date_val = datetime.date(int(year), int(month), int(day)) + day_of_year = date_val.toordinal() - datetime.date(date_val.year, 1, 1).toordinal() + 1 + print("Record created for day of year %d" % day_of_year) + print("%s - %s - %s: %s " % (year, month, day, amount)) + balances[day_of_year] = balances[day_of_year] + Decimal(amount) + + current_balance = open_balance + day_being_processed = 1 + accumlated_interest = Decimal('0.000000') + + while (day_being_processed <= days_in_year): + print("Day of year: %d" % day_being_processed) + #calculate the day's balance + current_balance = current_balance + balances[day_being_processed-1] + balances[day_being_processed-1] = current_balance + + #accummulate the day's interest + todays_interest = current_balance * interest_rate + accumlated_interest = accumlated_interest + todays_interest + + #check if this is the last day of the month, if it is do the transition stuff + current_month = datetime.date.fromordinal(start_date.toordinal()+day_being_processed-1).month + next_month = datetime.date.fromordinal(start_date.toordinal()+day_being_processed).month + print("Current Month: %d Next_month: %d" % (current_month, next_month )) + if (current_month != next_month): + interest_invoices[current_month-1] = accumlated_interest.quantize(Decimal('.01')) + current_balance = current_balance + accumlated_interest + accumlated_interest = Decimal('0.00') + + + day_being_processed = day_being_processed + 1 + + i=0 + while (i<12): + print ("%d: %s" %(i+1, interest_invoices[i]) ) + i = i +1 + +if __name__ == '__main__': + main() diff --git a/mortgage/mortgage_template.py b/mortgage/mortgage_template.py deleted file mode 100644 index dfa8260..0000000 --- a/mortgage/mortgage_template.py +++ /dev/null @@ -1,345 +0,0 @@ -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 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 - - -def transformTemplate(template_fileName, loanModel): - # template_filename = "statement.text.jinja" - # setup jinja for creating the statement - env = Environment(loader=FileSystemLoader('.')) - template = env.get_template(template_fileName) - - print loanModel - report = template.render(model=loanModel) - return report - - -def generatePDFAttachment(): - template_filename = "statement.pdf.jinja" - pass - - -def generateEmail(from_address, to_address, subject, body, pdfAttachment, txtAttachment): - msg = MIMEMultipart() - msg['Subject'] = subject - msg['From'] = from_address - msg['To'] = to_address - - msg.attach(MIMEText(body)) - - if (pdfAttachment != None): - part = MIMEBase("application", "octet-stream") - part.set_payload(pdfAttachment) - Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"') - msg.attach(part) - - if (txtAttachment != None): - part = MIMEBase("text", "html") - part.set_payload(txtAttachment) - Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="statement.html"') - msg.attach(part) - - return msg - -def generateEmailWithAttachments(from_address, to_address, subject, body, pdfAttachment, htmlAttachment, txtAttachment): - msg = MIMEMultipart() - msg['Subject'] = subject - msg['From'] = from_address - msg['To'] = to_address - - msg.attach(MIMEText(body)) - - if (pdfAttachment != None): - part = MIMEBase("application", "octet-stream") - part.set_payload(pdfAttachment) - Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"') - msg.attach(part) - - if (txtAttachment != None): - part = MIMEBase("text", "html") - part.set_payload(txtAttachment) - Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="statement.html"') - 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(loan): - templateKey = loan["datastore"]["format"] + "Template" - if templateKey in loan: - template = loan[templateKey] - else: - template = 'statement.pdf.jinja' - - return template - -def main(): - test_flag = True - # 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. - - # read in the file - test_flag = False - test_address = 'jkent3rd@yahoo.com' - - #filename = "./testloan.json" - filename = "./9Kloan.json" - #filename = "./dadmortgage.json" - #filename = "./brendamortgage.json" - #filename = "./greenfield_mortgage.json" - template_filename = "statement.pdf.jinja" - - loan = loadLoanInformation(filename) - - amortizeLoan(loan) - - report = transformTemplate(selectTemplate(loan), loan) - - if loan["email"]["send_pdf"] == "true": - pdfAttachment = createPDF(report) - - - # send email - emailParameters = loan["email"] - msg = generateEmail(emailParameters["from_address"], emailParameters["to_address"], emailParameters["subject"], - emailParameters["body"], pdfAttachment, report) - if test_flag == False: - sendEmail(msg, emailParameters["from_address"], emailParameters["to_address"], emailParameters['password']) - else: - sendEmail(msg, emailParameters["from_address"], test_address, emailParameters['password']) - -if __name__ == '__main__': - main() diff --git a/mortgage/templates/main.html b/mortgage/templates/main.html index 1b0c7db..29e8664 100644 --- a/mortgage/templates/main.html +++ b/mortgage/templates/main.html @@ -1,7 +1,6 @@ - -

+

+
## Due DateDays InterestDays Interest Payment Amt Principal Pmt Interest Pmt
Term: {{model.parameters.periods }} months
Next Payment Due Date: {{model.parameters.next_due_date}}
Payment Due: {{ "$%.2f"|format(model.parameters.next_payment_amt) }}
Daily Interest Accrual: {{ "$%.2f"|format(model.current_daily_interest_accrual) }}
-

+

@@ -79,7 +80,7 @@ - @@ -104,18 +105,18 @@ {% if item.month == 12 or loop.last %} - + {% endif %} {% endfor %} - +
Loan History
# + # Due Date Date Paid Days Interest {{ "$%.2f"|format(item.new_balance) }}
Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.
Total interest paid in {{item.year}} is {{ "$%.2f"|format(item.annual_interest_to_date) }}.
Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.
Total interest paid to date is {{ "$%.2f"|format(model.total_interest_paid_to_date) }}.

Loan Accounting

+ Message: Send Statement As: @@ -187,7 +188,7 @@ - + @@ -203,7 +204,7 @@
Record a Payment
Payment Date:
Payment Amount:
Late Fee Amount:
- + diff --git a/mortgage/web.py b/mortgage/web.py index f28db28..d737b22 100644 --- a/mortgage/web.py +++ b/mortgage/web.py @@ -1,4 +1,3 @@ -import json import jinja2 import smtplib import os @@ -13,19 +12,19 @@ from email import encoders import couchdb try: - dbUserName = os.environ['COUCHDB_USERNAME'] + dbUserName = os.environ['COUCHDB_USERNAME'] except: - dbUserName = 'admin' + dbUserName = 'admin' try: - dbPassword = os.environ['COUCHDB_PW'] + dbPassword = os.environ['COUCHDB_PW'] except: - dbPassword = 'ams19230' + dbPassword = 'ams19230' try: - dbHost = os.environ['COUCHDB_HOST'] + dbHost = os.environ['COUCHDB_HOST'] except: - dbHost = 'couch.jkent.org' + dbHost = 'couch.jkent.org' connectString = f'https://{dbUserName}:{dbPassword}@{dbHost}/' print(connectString) @@ -50,7 +49,7 @@ app = Flask(__name__) @app.route('/') def hello(): - loans = getLoanFiles(database) + loans = getLoanFiles() #if a loan was not specified, choose the first loan and reload page with it if 'loan' in request.args: @@ -117,7 +116,7 @@ def update_file(): if proceed_flag is True: payment_history.append([payment_date, str(payment_amount), str(late_fee), str(extra_payment)]) - database[document_id]=document + database[document_id] = document messages.append("The payment was successfully written. ") # see if an email notification was requested, if not, exit now @@ -189,7 +188,7 @@ def send_statement(): ###### -def getLoanFiles(directory): +def getLoanFiles(): #iterate over each of the documents found in the mortgage couchdb #database and pull their display name loans = [] @@ -201,25 +200,26 @@ def getLoanFiles(directory): return loans + def createLoanEntryMenuItem(loanName, document_id): x = {} x['name'] = loanName x['document_id'] = document_id return x + ###### # Datastore Manipulation Functions ###### - def getStatementHeader(datastore): return datastore['header'] def getAccounting(datastore): try: - return datastore['accounting'] + return datastore['accounting'] except: - return {"payment_account":"", "loan_account":"", "interest_account": ""} + return {"payment_account": "", "loan_account": "", "interest_account": ""} def getEmailInformation(datastore): @@ -281,17 +281,16 @@ def getBorrower(datastore): def getDatastore(document_id=None): - if document_id == None: + if document_id is None: quit("Invalid document.") datastore = database[document_id] return datastore + ###### # Loan Calculation Functions ###### - - def amortizeLoan(loan): # loop over the payments and calculate the actual amortization monthly_payment = loan["parameters"]["monthly_payment"] @@ -331,7 +330,8 @@ def amortizeLoan(loan): % (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_daily_interest = (remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) + new_interest = (days_since_last_payment * new_daily_interest).quantize(Decimal("0.00")) new_principal = payment_amount - new_interest - late_fee interest_paid_through_date = payment_date total_interest = total_interest + new_interest @@ -346,8 +346,8 @@ def amortizeLoan(loan): payment_record['bill_date'] = next_bill_date payment_record['payment_date'] = payment_date payment_record['days_of_interest'] = days_since_last_payment + payment_record['daily_interest'] = new_daily_interest payment_record['payment_amount'] = payment_amount - principal_payment = payment_amount - new_interest - late_fee payment_record['principal_payment'] = payment_amount - new_interest - late_fee payment_record['interest_payment'] = new_interest payment_record['new_balance'] = remaining_principal @@ -388,6 +388,7 @@ def amortizeLoan(loan): # 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_daily_interest = (remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) # make sure the last payment isn't too much @@ -405,6 +406,7 @@ def amortizeLoan(loan): 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['daily_interest'] = new_daily_interest future_payment_record['payment_amount'] = monthly_payment future_payment_record['principal_payment'] = new_principal future_payment_record['interest_payment'] = new_interest @@ -417,7 +419,7 @@ def amortizeLoan(loan): 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["current_daily_interest_accrual"] = (remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) loan["balloon_payment"] = remaining_principal loan["past_payments"] = past_payments loan["future_payments"] = future_payments @@ -529,7 +531,7 @@ def selectTemplate(formatType): def main(): app.debug = True - app.run(host = '0.0.0.0', port='8000') + app.run(host='0.0.0.0', port=8000) if __name__ == '__main__': From b5645e2492fb7babe154771b5f8c18112494c431 Mon Sep 17 00:00:00 2001 From: john Date: Mon, 20 May 2024 18:15:36 -0400 Subject: [PATCH 38/38] Updated files to produce current daily interest accrual information. Fixed calculation bug. Added current balance to loan information screen. --- mortgage/templates/main.html | 1 + mortgage/web.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mortgage/templates/main.html b/mortgage/templates/main.html index 72e004e..0e457da 100644 --- a/mortgage/templates/main.html +++ b/mortgage/templates/main.html @@ -68,6 +68,7 @@ +
Record an Extra Payment
Payment Date:
Payment Amount:
Send Payment Recorded Notification
Term: {{model.parameters.periods }} months
Next Payment Due Date: {{model.parameters.next_due_date}}
Payment Due: {{ "$%.2f"|format(model.parameters.next_payment_amt) }}
Current Balance {{ "$%.2f"|format(model.current_balance) }}
Daily Interest Accrual: {{ "$%.2f"|format(model.current_daily_interest_accrual) }}
diff --git a/mortgage/web.py b/mortgage/web.py index d737b22..e040033 100644 --- a/mortgage/web.py +++ b/mortgage/web.py @@ -385,6 +385,9 @@ def amortizeLoan(loan): else: loan["parameters"]["next_payment_amt"] = monthly_payment + loan["current_balance"] = remaining_principal + loan["current_daily_interest_accrual"] = (remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) + # 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 @@ -419,7 +422,7 @@ def amortizeLoan(loan): 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["current_daily_interest_accrual"] = (remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) + loan["balloon_payment"] = remaining_principal loan["past_payments"] = past_payments loan["future_payments"] = future_payments