From d0d167b158ea5ce731e49e0df0d31a3ba9594e3c Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 12 May 2017 16:46:15 -0400 Subject: [PATCH 1/8] Improve warning messages for income statement. --- .../static/financialInputs/scripts/app.js | 170 ++++++++++-------- .../financialInputs/cashBalance.html | 6 +- .../financialInputs/incomeStatement.html | 8 +- blocnote/apps/financialInputs/views.py | 70 +++++--- 4 files changed, 157 insertions(+), 97 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 3622a0a..d698e7e 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -794,6 +794,11 @@ function addCashBalanceRow(balance, date, isFromBalanceSheet) { return false; } +var cashBalanceForm = document.querySelector('#cash-balance-form'); +cashBalanceForm.onchange = function() { + document.querySelector('cash-balance-message').innerHTML = ``; +} + /**Make PUT request with data from the cash balance table. */ function submitCashBalanceForm(form) { var myResult = []; @@ -804,13 +809,21 @@ function submitCashBalanceForm(form) { record['balance'] = table.rows[rowIndex].cells[1].children[0].value; date = table.rows[rowIndex].cells[2].children[0].value; dateSplit = date.split('-'); + if (dateSplit.length === 1) { + document.querySelector('cash-balance-message').innerHTML = ` + Please fill in data! + `; + return false; + } dateDict = { 'day': Number(dateSplit[2]), 'month': Number(dateSplit[1]), 'year': Number(dateSplit[0]), } if (!validateDate(dateDict, todaysDate)) { - alert('Statement Date cannot be in the future'); + document.querySelector('cash-balance-message').innerHTML = ` + Statement date cannot be in the future! + `; return false; } record['statement-date'] = date; @@ -853,9 +866,9 @@ function loadIncomeStatemenHeading() { heading.innerHTML = ` Year - - - + + + Next Year Average @@ -870,17 +883,17 @@ function loadIncomeStatemenBody() { body.innerHTML = ` Revenue - - - + + + Utility Expense - - - + + + @@ -934,9 +947,9 @@ function loadIncomeStatemenBody() { Non-Utility Operating Expense - - - + + + @@ -976,7 +989,7 @@ function loadIncomeStatementDropdownBox(cagr) { var text = ``; text += ` Please select Growth Rate - `; @@ -1036,6 +1049,11 @@ function getIncomeStatementTable() { }); } +var incomeStatementForm = document.querySelector('#income-statement-form'); +incomeStatementForm.onchange = function() { + document.querySelector('#income-statement-error-msg').innerHTML = ``; +} + /** * Fire up when the calculate button is hit. Get all the input data from the table and the growth rate value * and make HTTP PUT request to the backend. Update the table with the calculated data received from the backend. @@ -1065,65 +1083,75 @@ function onCalculateIncomeStatement(data) { 'X-CSRFToken': Cookies.get('csrftoken') }) }).then(res => { - const REVENUE_ROW = 0; - const UTILITY_EXPENSE_ROW = 1; - const ENERGY_EXPENSE_ROW = 2; - const ELECTRICITY_ROW = 3; - const GAS_ROW = 4; - const OIL_ROW = 5; - const WATER_ROW = 6; - const OTHER_UTILITY_EXPENSE_ROW = 7; - const NON_UTILITY_OPERATING_EXPENSE_ROW = 8; - const NET_NON_ENERGY_EXPENSE_ROW = 9; - const TOTAL_EXPENSE_ROW = 10; - const NET_OPERATING_EXPENSE = 11; - var incomeStatementFull = res.payload.instance; - for (key in incomeStatementFull) { - cellIndex = Number(key)+1; - updateIncomeStatementTable(ENERGY_EXPENSE_ROW, cellIndex, incomeStatementFull[key].energy_opex); - updateIncomeStatementTable(ELECTRICITY_ROW, cellIndex, incomeStatementFull[key].electricity_opex); - updateIncomeStatementTable(GAS_ROW, cellIndex, incomeStatementFull[key].gas_opex); - updateIncomeStatementTable(OIL_ROW, cellIndex, incomeStatementFull[key].oil_opex); - updateIncomeStatementTable(WATER_ROW, cellIndex, incomeStatementFull[key].water_opex); - updateIncomeStatementTable(OTHER_UTILITY_EXPENSE_ROW, cellIndex, incomeStatementFull[key].other_utility); - updateIncomeStatementTable(NET_NON_ENERGY_EXPENSE_ROW, cellIndex, incomeStatementFull[key].net_non_energy_opex); - updateIncomeStatementTable(TOTAL_EXPENSE_ROW, cellIndex, incomeStatementFull[key].total_opex); - updateIncomeStatementTable(NET_OPERATING_EXPENSE, cellIndex, incomeStatementFull[key].noi); - } - var future = res.payload.future; - const FUTURE_INDEX = 4; - heading = document.querySelector('#income-statement-table thead'); - heading.rows[0].cells[FUTURE_INDEX].innerHTML = future.year; - updateIncomeStatementTable(REVENUE_ROW,FUTURE_INDEX,future.revenue); - updateIncomeStatementTable(UTILITY_EXPENSE_ROW,FUTURE_INDEX,future.utility_expense); - updateIncomeStatementTable(ENERGY_EXPENSE_ROW,FUTURE_INDEX,future.energy_opex); - updateIncomeStatementTable(ELECTRICITY_ROW,FUTURE_INDEX,future.electricity_opex); - updateIncomeStatementTable(GAS_ROW,FUTURE_INDEX,future.gas_opex); - updateIncomeStatementTable(OIL_ROW,FUTURE_INDEX,future.oil_opex); - updateIncomeStatementTable(WATER_ROW,FUTURE_INDEX,future.water_opex); - updateIncomeStatementTable(OTHER_UTILITY_EXPENSE_ROW,FUTURE_INDEX,future.other_utility); - updateIncomeStatementTable(NON_UTILITY_OPERATING_EXPENSE_ROW,FUTURE_INDEX,future.non_utility_expense); - updateIncomeStatementTable(NET_NON_ENERGY_EXPENSE_ROW,FUTURE_INDEX,future.net_non_energy_opex); - updateIncomeStatementTable(TOTAL_EXPENSE_ROW,FUTURE_INDEX,future.total_opex); - updateIncomeStatementTable(NET_OPERATING_EXPENSE,FUTURE_INDEX,future.noi); + if (!res.payload.err) { + const REVENUE_ROW = 0; + const UTILITY_EXPENSE_ROW = 1; + const ENERGY_EXPENSE_ROW = 2; + const ELECTRICITY_ROW = 3; + const GAS_ROW = 4; + const OIL_ROW = 5; + const WATER_ROW = 6; + const OTHER_UTILITY_EXPENSE_ROW = 7; + const NON_UTILITY_OPERATING_EXPENSE_ROW = 8; + const NET_NON_ENERGY_EXPENSE_ROW = 9; + const TOTAL_EXPENSE_ROW = 10; + const NET_OPERATING_EXPENSE = 11; + var incomeStatementFull = res.payload.instance; + for (key in incomeStatementFull) { + cellIndex = Number(key)+1; + updateIncomeStatementTable(ENERGY_EXPENSE_ROW, cellIndex, incomeStatementFull[key].energy_opex); + updateIncomeStatementTable(ELECTRICITY_ROW, cellIndex, incomeStatementFull[key].electricity_opex); + updateIncomeStatementTable(GAS_ROW, cellIndex, incomeStatementFull[key].gas_opex); + updateIncomeStatementTable(OIL_ROW, cellIndex, incomeStatementFull[key].oil_opex); + updateIncomeStatementTable(WATER_ROW, cellIndex, incomeStatementFull[key].water_opex); + updateIncomeStatementTable(OTHER_UTILITY_EXPENSE_ROW, cellIndex, incomeStatementFull[key].other_utility); + updateIncomeStatementTable(NET_NON_ENERGY_EXPENSE_ROW, cellIndex, incomeStatementFull[key].net_non_energy_opex); + updateIncomeStatementTable(TOTAL_EXPENSE_ROW, cellIndex, incomeStatementFull[key].total_opex); + updateIncomeStatementTable(NET_OPERATING_EXPENSE, cellIndex, incomeStatementFull[key].noi); + } + var future = res.payload.future; + const FUTURE_INDEX = 4; + heading = document.querySelector('#income-statement-table thead'); + heading.rows[0].cells[FUTURE_INDEX].innerHTML = future.year; + updateIncomeStatementTable(REVENUE_ROW,FUTURE_INDEX,future.revenue); + updateIncomeStatementTable(UTILITY_EXPENSE_ROW,FUTURE_INDEX,future.utility_expense); + updateIncomeStatementTable(ENERGY_EXPENSE_ROW,FUTURE_INDEX,future.energy_opex); + updateIncomeStatementTable(ELECTRICITY_ROW,FUTURE_INDEX,future.electricity_opex); + updateIncomeStatementTable(GAS_ROW,FUTURE_INDEX,future.gas_opex); + updateIncomeStatementTable(OIL_ROW,FUTURE_INDEX,future.oil_opex); + updateIncomeStatementTable(WATER_ROW,FUTURE_INDEX,future.water_opex); + updateIncomeStatementTable(OTHER_UTILITY_EXPENSE_ROW,FUTURE_INDEX,future.other_utility); + updateIncomeStatementTable(NON_UTILITY_OPERATING_EXPENSE_ROW,FUTURE_INDEX,future.non_utility_expense); + updateIncomeStatementTable(NET_NON_ENERGY_EXPENSE_ROW,FUTURE_INDEX,future.net_non_energy_opex); + updateIncomeStatementTable(TOTAL_EXPENSE_ROW,FUTURE_INDEX,future.total_opex); + updateIncomeStatementTable(NET_OPERATING_EXPENSE,FUTURE_INDEX,future.noi); - var averageDict = res.payload.average; - const AVERAGE_DICT_INDEX = 5; - updateIncomeStatementTable(REVENUE_ROW,AVERAGE_DICT_INDEX,averageDict.revenue); - updateIncomeStatementTable(UTILITY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.utility_expense); - updateIncomeStatementTable(ENERGY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.energy_opex); - updateIncomeStatementTable(ELECTRICITY_ROW,AVERAGE_DICT_INDEX,averageDict.electricity_opex); - updateIncomeStatementTable(GAS_ROW,AVERAGE_DICT_INDEX,averageDict.gas_opex); - updateIncomeStatementTable(OIL_ROW,AVERAGE_DICT_INDEX,averageDict.oil_opex); - updateIncomeStatementTable(WATER_ROW,AVERAGE_DICT_INDEX,averageDict.water_opex); - updateIncomeStatementTable(OTHER_UTILITY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.other_utility); - updateIncomeStatementTable(NON_UTILITY_OPERATING_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.non_utility_expense); - updateIncomeStatementTable(NET_NON_ENERGY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.net_non_energy_opex); - updateIncomeStatementTable(TOTAL_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.total_opex); - updateIncomeStatementTable(NET_OPERATING_EXPENSE,AVERAGE_DICT_INDEX,averageDict.noi); + var averageDict = res.payload.average; + const AVERAGE_DICT_INDEX = 5; + updateIncomeStatementTable(REVENUE_ROW,AVERAGE_DICT_INDEX,averageDict.revenue); + updateIncomeStatementTable(UTILITY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.utility_expense); + updateIncomeStatementTable(ENERGY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.energy_opex); + updateIncomeStatementTable(ELECTRICITY_ROW,AVERAGE_DICT_INDEX,averageDict.electricity_opex); + updateIncomeStatementTable(GAS_ROW,AVERAGE_DICT_INDEX,averageDict.gas_opex); + updateIncomeStatementTable(OIL_ROW,AVERAGE_DICT_INDEX,averageDict.oil_opex); + updateIncomeStatementTable(WATER_ROW,AVERAGE_DICT_INDEX,averageDict.water_opex); + updateIncomeStatementTable(OTHER_UTILITY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.other_utility); + updateIncomeStatementTable(NON_UTILITY_OPERATING_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.non_utility_expense); + updateIncomeStatementTable(NET_NON_ENERGY_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.net_non_energy_opex); + updateIncomeStatementTable(TOTAL_EXPENSE_ROW,AVERAGE_DICT_INDEX,averageDict.total_opex); + updateIncomeStatementTable(NET_OPERATING_EXPENSE,AVERAGE_DICT_INDEX,averageDict.noi); - var box = document.querySelector('#growth-rate option'); - box.innerHTML = `CAGR ${res.payload.CAGR}`; + var box = document.querySelector('#growth-rate option'); + box.innerHTML = `CAGR ${res.payload.CAGR}`; + document.querySelector('#income-statement-error-msg').innerHTML = ` + Saved Successfully. + `; + } + else { + document.querySelector('#income-statement-error-msg').innerHTML = ` + ${res.payload.err} + `; + } }) return false; } diff --git a/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html b/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html index 0a3dd92..7cf6f8b 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html @@ -2,7 +2,11 @@ Cash Balance
- +
+ +
+
+
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html b/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html index f1ce56c..bbb9629 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html @@ -2,11 +2,13 @@

Income Statements

- +
-
- +
+ +
+
diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index d6a160a..85dec31 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -1,8 +1,10 @@ import json from datetime import date +import datetime from django.shortcuts import render from django.http import JsonResponse +from django.http import HttpResponseBadRequest from django.db import connections from django.views import View @@ -738,7 +740,7 @@ class IncomeStatementTable(View): model = IncomeStatement - def convert_response_format(self, put): + def convert_response_format(self, put, pro_forma_year): """Convert PUT body format. Convert the data in HTTP PUT request into format accepted by bpfin. Obtain the growth rate as well. @@ -750,16 +752,24 @@ class IncomeStatementTable(View): result: Dictionary with year as key and income statement information dictionary as value. growth rate: The selected growth rate to project the future income statement. """ + now_year = int(datetime.datetime.now().year) + print(now_year) result = {} - for record in put: - if 'growth-rate' not in record: - value = {} - value['revenue'] = float(record['revenue']) - value['utility_expense'] = float(record['utility-expense']) - value['non_utility_expense'] = float(record['non-utility-operating-expense']) - result[int(record['year'])] = value - else: - growth_rate = float(record['growth-rate']) + prev_year = int(put[0]['year']) + for index in range(3): + cur_year = int(put[index]['year']) + print(cur_year) + if cur_year < pro_forma_year: + raise ValueError('A year before proforma start year was entered in column %s!' % (index + 1)) + if (index > 0 and prev_year >= cur_year) or (cur_year >= now_year): + raise ValueError('Year in column %s should be greater than year in column %s!' % ((index + 1), index)) + value = {} + value['revenue'] = float(put[index]['revenue']) + value['utility_expense'] = float(put[index]['utility-expense']) + value['non_utility_expense'] = float(put[index]['non-utility-operating-expense']) + result[int(put[index]['year'])] = value + prev_year = cur_year + growth_rate = float(put[3]['growth-rate']) return result, growth_rate def get_bills_overview_data(self, building_id): @@ -793,7 +803,9 @@ class IncomeStatementTable(View): 'oil': [oil, not obj.oil_is_user_input], 'water': [water, not obj.water_is_user_input], } - return bill_overview + return bill_overview + else: + raise ValueError def save(self, result, building_id): """Save income statement data in database. @@ -830,23 +842,37 @@ class IncomeStatementTable(View): """ put = json.loads(request.body.decode()) + # Get bills overview data from the database. + try: + bill_overview = self.get_bills_overview_data(building_id) + except: + return JsonResponse({'err': 'There are no bills projected for this building'}) + + # Get the analysis date from the financing overview model. + try: + financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) + analysis_date = { + 'proforma_start': financing_overview_obj.pro_forma_start_date, + 'proforma_duration': financing_overview_obj.pro_forma_duration, + } + except: + return JsonResponse({'err': 'Please fill in the proforma information form at the top!'}) + # Convert the response body to the format bpfin accepts. - raw_income_statement_input, growth_rate = self.convert_response_format(put) + try: + raw_income_statement_input, growth_rate = self.convert_response_format( + put, + int(analysis_date['proforma_start'].year) + ) + except ValueError as err: + return JsonResponse({'err': err.args[0]}) + GrowthRate.objects.filter(building_id=building_id).delete() GrowthRate.objects.create( building_id=building_id, growth_rate=float("{0:.2f}".format(growth_rate)), ) - # Get bills overview data from the database. - bill_overview = self.get_bills_overview_data(building_id) - - # Get the analysis date from the financing overview model. - financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) - analysis_date = { - 'proforma_start': financing_overview_obj.pro_forma_start_date, - 'proforma_duration': financing_overview_obj.pro_forma_duration, - } # organize_bill_overview takes in the bills overview and analysis date and fills in average values in places # where there are no values. bill_overview_organized = organize_bill_overview(bill_overview, analysis_date) @@ -902,7 +928,7 @@ class IncomeStatementTable(View): average_dict['total_opex'] = float("{0:.2f}".format(average_dict['total_opex'])) average_dict['noi'] = float("{0:.2f}".format(average_dict['noi'])) - cagr = float("{0:.2f}".format(cagr)) + cagr = ('= ' + "{0:.2f}".format(cagr * 100) + '%') return JsonResponse({'instance': result, 'future': result_dict, 'average': average_dict, 'CAGR': cagr}) def get(self, request, building_id): -- GitLab From 5ab1f9323249afb79cece40444d3cfbb9bd11517 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 10:37:20 -0400 Subject: [PATCH 2/8] Validate all the forms and display error, warning and success messages. --- .../static/financialInputs/scripts/app.js | 165 +++++++++++++----- .../financialInputs/billsOverview.html | 2 + .../financialInputs/cashBalance.html | 6 +- .../financialInputs/customerPreference.html | 2 + .../financialInputs/liabilities.html | 2 + blocnote/apps/financialInputs/views.py | 86 +++++---- 6 files changed, 176 insertions(+), 87 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index d698e7e..abca2f6 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -136,32 +136,38 @@ function billProjectionDatesForm(form) { 'X-CSRFToken': Cookies.get('csrftoken') }) }).then(res => { - // Display response message. Add that loan options changed if fund was changed. Reset didFundChange to false. - responseMessage = res.payload.msg; - if (didFundChange) { - responseMessage += ' Loan Options table is reloded.'; + if (!res.payload.err) { + // Display response message. Add that loan options changed if fund was changed. Reset didFundChange to false. + responseMessage = 'Saved'; + if (didFundChange) { + responseMessage += ' Loan Options table is reloded.'; + } + var resMsg = document.querySelector('#pro-forma-form-save-msg'); + resMsg.innerHTML = `${responseMessage}`; + /** + * delete-loan-options request to the backend to delete loan options for this building id if previosly stored. + * This ensures that the loan options table is up to date with the latest fund. Clear the loan options table + * and reload with new fund's default loan options. + */ + if (didFundChange) { + didFundChange = false; + request(`loan-options/?loans=delete-loan-options`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + }, + }).then(res => { + var tableBody = document.querySelector('#loan-options-table tbody'); + tableBody.innerHTML = ``; + }); + } + getLoanOptionsTable(); } - var resMsg = document.querySelector('#pro-forma-form-save-msg'); - resMsg.innerHTML = `${responseMessage}`; - /** - * delete-loan-options request to the backend to delete loan options for this building id if previosly stored. - * This ensures that the loan options table is up to date with the latest fund. Clear the loan options table - * and reload with new fund's default loan options. - */ - if (didFundChange) { - didFundChange = false; - request(`loan-options/?loans=delete-loan-options`, { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }).then(res => { - var tableBody = document.querySelector('#loan-options-table tbody'); - tableBody.innerHTML = ``; - }); + else { + var resMsg = document.querySelector('#pro-forma-form-save-msg'); + resMsg.innerHTML = `${res.payload.err}`; } - getLoanOptionsTable(); }); } return false; @@ -289,7 +295,7 @@ function createEstimateModelForm() { var estimateModelForm = ``; estimateModelForm += ` -
`; +
`; estimateModelForm += estimationDropDownBox(); estimateModelForm += createCalculateButton(); estimateModelForm += ` @@ -482,7 +488,7 @@ function billsOverviewFormCalculate(data) { }) }).then(res => { if (res.payload.instance['err']) { - alert(res.payload.instance['msg']); + alert(res.payload.instance['err']); return false; } table = document.querySelector('#Energy-Bills-Overview') @@ -494,12 +500,21 @@ function billsOverviewFormCalculate(data) { return false; } +var billsOverview = document.querySelector('#Energy-Bills-Overview'); +billsOverview.onchange = function() { + document.querySelector('#bills-overview-warning-message').innerHTML = ''; +} + function billsOverviewFormSubmit(form) { const formData = new FormData(form); const result = {}; for (const [key, value] of formData.entries()) { result[key] = value; } + if (!(document.querySelector('#estimation-model').value === 'Rough Estimation')) { + alert('Please recalculate the projection with rough estimation and try to save again'); + return false; + } request(`bills-overview/`, { method: 'POST', credentials: 'same-origin', @@ -508,10 +523,39 @@ function billsOverviewFormSubmit(form) { 'Content-Type': 'application/json', 'X-CSRFToken': Cookies.get('csrftoken') }) + }).then(res => { + var message = ''; + if (!res.payload.err) { + message = 'Saved'; + } + else { + message = res.payload.err; + } + document.querySelector('#bills-overview-warning-message').innerHTML = ` + ${message} + `; + var totalCharge = [] + var billsOverviewTable = document.querySelector('#table-bills-overview tbody'); + var columnLength = billsOverviewTable.rows[0].cells.length - 2; + for (var cellIndex = 2; cellIndex < columnLength; cellIndex++) { + val = 0; + for(var rowIndex = 0; rowIndex < 4; rowIndex++) { + val += Number(billsOverviewTable.rows[rowIndex].cells[cellIndex].children[0].value); + } + totalCharge.push(val); + } + for (var cellIndex = 2; cellIndex < columnLength; cellIndex++) { + billsOverviewTable.rows[4].cells[cellIndex].innerHTML = totalCharge[cellIndex - 2]; + } }); return false; } +var customerPreference = document.querySelector('#Customer-Preference'); +customerPreference.onchange = function() { + document.querySelector('#customer-preference-warning-message').innerHTML = ''; +} + /** * Handle customer preference form submit. Make a HTTP PUT request. */ @@ -530,6 +574,12 @@ function customerPreferenceForm(form) { 'Content-Type': 'application/json', 'X-CSRFToken': Cookies.get('csrftoken') }) + }).then(res => { + if (!res.payload.err) { + document.querySelector('#customer-preference-warning-message').innerHTML = ` + Saved + `; + } }); return false; } @@ -559,15 +609,15 @@ function createCustomerPreferenceTable(instance) {
- + - + - +
Affordable Downpayment
Expected PaybackMonthsMonths
Expected Net NOI DSCR
@@ -632,7 +682,7 @@ function addLiabilitiesRow(lender, service, term, date) { `; cell = row.insertCell(4); cell.innerHTML = ` - + `; cell = row.insertCell(5); cell.innerHTML = ` @@ -657,6 +707,11 @@ function deleteLiabilitiesRow(rowIndex) { return false; } +var liabilitiesForm = document.querySelector('#Liabilities'); +liabilitiesForm.onchange = function() { + document.querySelector('#liabilities-warning-message').innerHTML = ''; +} + /**HTTP PUT request with the user inputs for Mortgage and Liability. */ function liabilitiesSubmitForm(form) { table = document.querySelector('#liabilities-table'); @@ -689,6 +744,12 @@ function liabilitiesSubmitForm(form) { 'Content-Type': 'application/json', 'X-CSRFToken': Cookies.get('csrftoken') }) + }).then(res => { + if (!res.payload.err) { + document.querySelector('#liabilities-warning-message').innerHTML = ` + Saved! + `; + } }); return false; } @@ -731,7 +792,7 @@ function loadCashBalanceTable(instance) { } } else { - addCashBalanceRow(0, '', false); + addCashBalanceRow('', '', false); } } @@ -779,11 +840,11 @@ function addCashBalanceRow(balance, date, isFromBalanceSheet) { cell.innerHTML = `${rowCount + 1}`; cell = row.insertCell(1); cell.innerHTML = ` - + `; cell = row.insertCell(2); cell.innerHTML = ` - + `; cell = row.insertCell(3); cell.innerHTML = getCashBalanceCheckBox(isFromBalanceSheet); @@ -799,6 +860,11 @@ cashBalanceForm.onchange = function() { document.querySelector('cash-balance-message').innerHTML = ``; } +var cashBalance = document.querySelector('#cash-balance-form'); +cashBalance.onchange = function() { + document.querySelector('#cash-balance-warning-message').innerHTML = ''; +} + /**Make PUT request with data from the cash balance table. */ function submitCashBalanceForm(form) { var myResult = []; @@ -809,19 +875,13 @@ function submitCashBalanceForm(form) { record['balance'] = table.rows[rowIndex].cells[1].children[0].value; date = table.rows[rowIndex].cells[2].children[0].value; dateSplit = date.split('-'); - if (dateSplit.length === 1) { - document.querySelector('cash-balance-message').innerHTML = ` - Please fill in data! - `; - return false; - } dateDict = { 'day': Number(dateSplit[2]), 'month': Number(dateSplit[1]), 'year': Number(dateSplit[0]), } if (!validateDate(dateDict, todaysDate)) { - document.querySelector('cash-balance-message').innerHTML = ` + document.querySelector('#cash-balance-warning-message').innerHTML = ` Statement date cannot be in the future! `; return false; @@ -844,6 +904,12 @@ function submitCashBalanceForm(form) { 'Content-Type': 'application/json', 'X-CSRFToken': Cookies.get('csrftoken') }) + }).then(res => { + if (!res.payload.err) { + document.querySelector('#cash-balance-warning-message').innerHTML = ` + Saved + `; + } }); return false; } @@ -1313,6 +1379,19 @@ function loadDefaultLoan(data) { }); } +var saveLoanOptions = document.querySelector('#loan-options-table-form'); +var NoiDscr = document.querySelector('#required-noi-dscr'); +var CashDscr = document.querySelector('#required-cash-dscr'); +NoiDscr.onchange = function() { + document.querySelector('#no-fund-msg').innerHTML = ''; +} +CashDscr.onchange = function() { + document.querySelector('#no-fund-msg').innerHTML = ''; +} +saveLoanOptions.onchange = function() { + document.querySelector('#no-fund-msg').innerHTML = ''; +} + /** * Save loan options table. Send the the rows in the form of a dictionary to the backend to store in the database. */ @@ -1348,6 +1427,12 @@ function submitLoanOptionsForm(form) { 'Content-Type': 'application/json', 'X-CSRFToken': Cookies.get('csrftoken') }) + }).then(res => { + if (!res.payload.err) { + document.querySelector('#no-fund-msg').innerHTML = ` + Saved + `; + } }); return false; } diff --git a/blocnote/apps/financialInputs/templates/financialInputs/billsOverview.html b/blocnote/apps/financialInputs/templates/financialInputs/billsOverview.html index a1e6de8..ffc70ef 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/billsOverview.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/billsOverview.html @@ -3,3 +3,5 @@
+
+
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html b/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html index 7cf6f8b..e383f8b 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/cashBalance.html @@ -4,8 +4,8 @@
-
-
+
+
@@ -21,6 +21,6 @@
-
+
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html b/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html index ec52556..c4188f4 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html @@ -3,4 +3,6 @@
+
+
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/liabilities.html b/blocnote/apps/financialInputs/templates/financialInputs/liabilities.html index 5b0b087..1ab73eb 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/liabilities.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/liabilities.html @@ -3,6 +3,8 @@
+
+
diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 85dec31..4514db4 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -17,6 +17,16 @@ from .models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPrefe from .models import Liabilities, CashBalance, IncomeStatement, LoanOptions, DefaultLoan, Lender, GrowthRate +def get_analysis_date(building_id): + """Fetch the proforma start date and duration from the database.""" + financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) + analysis_date = { + 'proforma_start': financing_overview_obj.pro_forma_start_date, + 'proforma_duration': int(financing_overview_obj.pro_forma_duration), + } + return analysis_date + + def get_model_object(model, building_id): """Function that returns a model object. @@ -139,9 +149,8 @@ class BlocNoteHeader(View): result = {} try: self.handle_form(put, building_id) - result['msg'] = 'The data was saved.' except: - result['msg'] = 'Sorry, the data could not be saved.' + result['err'] = 'Sorry, the data could not be saved.' return JsonResponse(result) def get(self, request, building_id): @@ -480,16 +489,12 @@ class BillsOverviewView(View): algorithm=put['Estimation Model'], ) projected_bills = {} - analysis_date = {} pro_forma_object = get_model_object(FinancingOverview, building_id) if not pro_forma_object: - projected_bills['err'] = True - projected_bills['msg'] = 'Please fill in bill projection date, period and funds form' + projected_bills['err'] = 'Please fill in bill projection date, period and funds form' return JsonResponse({'instance': projected_bills}) - analysis_date['proforma_start'] = pro_forma_object.pro_forma_start_date - analysis_date['proforma_duration'] = int(pro_forma_object.pro_forma_duration) + analysis_date = get_analysis_date(building_id) if put['Estimation Model'] == 'Rough Estimation': - projected_bills['err'] = False for util in self.utility: projected_bills[util] = get_total_charge(util, analysis_date, building_id) e_user, g_user, o_user, w_user = get_if_user_input(building_id) @@ -508,8 +513,7 @@ class BillsOverviewView(View): total_annual_charge.append(total) projected_bills['total_annual_charge'] = total_annual_charge else: - projected_bills['err'] = True - projected_bills['msg'] = 'Only Rough Estimation available at this point' + projected_bills['err'] = 'Only Rough Estimation available at this point' return JsonResponse({'instance': projected_bills}) def post(self, request, building_id): @@ -527,31 +531,31 @@ class BillsOverviewView(View): """ post = json.loads(request.body.decode()) self.model_bills_overview.objects.filter(building_id=building_id).delete() - analysis_date = {} e_user, g_user, o_user, w_user = get_if_user_input(building_id) financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) - analysis_date['proforma_start'] = financing_overview_obj.pro_forma_start_date - analysis_date['proforma_duration'] = int(financing_overview_obj.pro_forma_duration) - for i in range(analysis_date['proforma_duration']): - store_year = str(analysis_date['proforma_start'].year + i) - e_search = 'electricity-value-' + store_year - g_search = 'gas-value-' + store_year - o_search = 'oil-value-' + store_year - w_search = 'water-value-' + store_year - self.model_bills_overview.objects.create( - building_id=building_id, - year=int(store_year), - electricity=post[e_search], - gas=post[g_search], - oil=post[o_search], - water=post[w_search], - electricity_is_user_input=e_user, - gas_is_user_input=g_user, - oil_is_user_input=o_user, - water_is_user_input=w_user - ) - - return JsonResponse({'status': 'OK'}) + analysis_date = get_analysis_date(building_id) + try: + for i in range(analysis_date['proforma_duration']): + store_year = str(analysis_date['proforma_start'].year + i) + e_search = 'electricity-value-' + store_year + g_search = 'gas-value-' + store_year + o_search = 'oil-value-' + store_year + w_search = 'water-value-' + store_year + self.model_bills_overview.objects.create( + building_id=building_id, + year=int(store_year), + electricity=post[e_search], + gas=post[g_search], + oil=post[o_search], + water=post[w_search], + electricity_is_user_input=e_user, + gas_is_user_input=g_user, + oil_is_user_input=o_user, + water_is_user_input=w_user + ) + except: + return JsonResponse({'err': 'Could not save table!'}) + return JsonResponse({}) class CustomerPreferenceView(View): @@ -588,7 +592,7 @@ class CustomerPreferenceView(View): expected_payback=put['Expected-Payback'], expected_net_noi_dscr=put['Expected-NOI-DSCR'] ) - return JsonResponse({'status': 'ok'}) + return JsonResponse({}) def get(self, request, building_id): """Fetch customer preference table. @@ -673,7 +677,7 @@ class LiabilitiesTable(View): monthly_service=float(record['monthly-service']), remaining_term=float(record['remaining-term']), ) - return JsonResponse({'status': 'OK'}) + return JsonResponse({}) class CashBalanceView(View): @@ -728,7 +732,7 @@ class CashBalanceView(View): is_from_balance_sheet=record['is-from-balance-sheet'], balance_amount=record['balance'], ) - return JsonResponse({'instance': 'OK'}) + return JsonResponse({}) class IncomeStatementTable(View): @@ -753,12 +757,10 @@ class IncomeStatementTable(View): growth rate: The selected growth rate to project the future income statement. """ now_year = int(datetime.datetime.now().year) - print(now_year) result = {} prev_year = int(put[0]['year']) for index in range(3): cur_year = int(put[index]['year']) - print(cur_year) if cur_year < pro_forma_year: raise ValueError('A year before proforma start year was entered in column %s!' % (index + 1)) if (index > 0 and prev_year >= cur_year) or (cur_year >= now_year): @@ -850,11 +852,7 @@ class IncomeStatementTable(View): # Get the analysis date from the financing overview model. try: - financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) - analysis_date = { - 'proforma_start': financing_overview_obj.pro_forma_start_date, - 'proforma_duration': financing_overview_obj.pro_forma_duration, - } + analysis_date = get_analysis_date(building_id) except: return JsonResponse({'err': 'Please fill in the proforma information form at the top!'}) @@ -1110,4 +1108,4 @@ class LoanOptionsTable(View): duration=record['duration'], max_loan_amount=record['maximum-loan-amount'] ) - return JsonResponse({'status': 'OK'}) + return JsonResponse({}) -- GitLab From b91b3a3dafa70b386a4440db61626cbc2d1be60f Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 11:02:35 -0400 Subject: [PATCH 3/8] Remove unnecessary pieces of code. --- .../static/financialInputs/scripts/app.js | 4 ++-- blocnote/apps/financialInputs/views.py | 15 +++++++-------- blocnote/apps/preliminaryFinance/views.py | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index abca2f6..f5dbd70 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -593,7 +593,7 @@ function createCustomerPreferenceTable(instance) { var expectedPayback = 999; var expectedNetNOIDSCR = 1.15 - if (instance.status) { + if (instance) { var downpayment = instance['downpayment']; var expectedPayback = instance['expected_payback']; var expectedNetNOIDSCR = instance['expected_net_noi_dscr'] @@ -783,7 +783,7 @@ function getCashBalanceForm() { /**Load cash balance table with initial values. Takes response from GET request to construct the table. */ function loadCashBalanceTable(instance) { table = document.querySelector('#cash-balance-table'); - if (instance.status) { + if (instance) { for (var record=0; record < instance.result.length; record++) { balanceAmount = Number(instance.result[record].balance_amount); date = instance.result[record].date; diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 4514db4..5f96ce8 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -167,12 +167,15 @@ class BlocNoteHeader(View): JsonResponse: Result with the financing overview data. """ financing_overview_objs = self.model.objects.filter(building_id=building_id) + + # Fetch all the funds. funds_obj = Fund.objects.all() funds = [] for fund_obj in funds_obj: funds.append((fund_obj.id, fund_obj.Name)) result = {} - result['present'] = False + + # Check if the financing overview entry exists in the database. if financing_overview_objs: financing_overview_obj = financing_overview_objs[0] result = { @@ -617,9 +620,7 @@ class CustomerPreferenceView(View): instance['downpayment'] = obj.downpayment instance['expected_payback'] = obj.expected_payback instance['expected_net_noi_dscr'] = obj.expected_net_noi_dscr - instance['status'] = True - else: - instance['status'] = False + return JsonResponse({'instance': instance}) @@ -696,17 +697,15 @@ class CashBalanceView(View): """ objs = self.model.objects.filter(building_id=building_id) instance = {} - instance['result'] = [] if objs: - instance['status'] = True + instance['result'] = [] for obj in objs: temp = {} temp['date'] = obj.statement_date temp['is_from_balance_sheet'] = obj.is_from_balance_sheet temp['balance_amount'] = obj.balance_amount instance['result'].append(temp) - else: - instance['status'] = False + return JsonResponse({'instance': instance}) def put(self, request, building_id): diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index 78560ec..1531d43 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -309,8 +309,8 @@ class Scenarios(View): raw_bill['date_to'].append(bill.date_to) raw_bill['usage'].append(float(bill.usage)) raw_bill['charge'].append(float(bill.charge)) - month_rough = prior_proj_rough_month(raw_bill, analysis_date) - prior_month_bill[utility_type] = month_rough + month_rough = prior_proj_rough_month(raw_bill, analysis_date) + prior_month_bill[utility_type] = month_rough return prior_month_bill def put(self, request, building_id): -- GitLab From 3877251539665712b148cf27e648228eea863f9e Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 12:14:50 -0400 Subject: [PATCH 4/8] Minor fixes. --- blocnote/apps/financialInputs/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 5f96ce8..310c1d9 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -620,8 +620,8 @@ class CustomerPreferenceView(View): instance['downpayment'] = obj.downpayment instance['expected_payback'] = obj.expected_payback instance['expected_net_noi_dscr'] = obj.expected_net_noi_dscr - - return JsonResponse({'instance': instance}) + return JsonResponse({'instance': instance}) + return JsonResponse({}) class LiabilitiesTable(View): @@ -705,8 +705,8 @@ class CashBalanceView(View): temp['is_from_balance_sheet'] = obj.is_from_balance_sheet temp['balance_amount'] = obj.balance_amount instance['result'].append(temp) - - return JsonResponse({'instance': instance}) + return JsonResponse({'instance': instance}) + return JsonResponse({}) def put(self, request, building_id): """Handle HTTP PUT request. -- GitLab From 0d0cdd4b1168f92233fc029a973a949c93550a57 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 14:41:19 -0400 Subject: [PATCH 5/8] Fix minor UI issues. --- .../static/financialInputs/scripts/app.js | 127 ++++++++++-------- .../static/preliminaryFinance/scripts/app.js | 10 +- 2 files changed, 83 insertions(+), 54 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index f5dbd70..5077e1d 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -112,63 +112,74 @@ function billProjectionDatesForm(form) { 'month': anticipatedCommissioningStartMonth, 'year': anticipatedCommissioningStartYear, } - // Ensure that commissioning date is after start date. - var validDate = validateDate(anticipatedConstructionStarDateDict, anticipatedCommissioningDateDict); - if (!validDate) { - alert("Anticipated Commissioning date has to be after Anticipated Construction start date"); + var proFormaStartYear = Number(proFormaStartDate.split('-')[0]); + var isProFormaDateCorrect = true; + if (todaysDate.year - proFormaStartYear < 3) { + isProFormaDateCorrect = confirm('ARE YOU SURE THE PROFORMA DATE IS CORRECT?'); } - else { - result = { - 'pro_forma_start_date': proFormaStartDate, - 'pro_forma_duration': proFormaDuration, - 'analysis_date': analysisDate, - 'fund': fund, - 'anticipated_construction_start_date': anticipatedConstructionStarDate, - 'anticipated_commissioning_date': anticipatedCommissioningDate, - 'anticipated_construction_period': anticipatedConstructionPeriod, + if (isProFormaDateCorrect) { + // Ensure that commissioning date is after start date. + var validDate = validateDate(anticipatedConstructionStarDateDict, anticipatedCommissioningDateDict); + if (!validDate) { + document.querySelector('#pro-forma-form-save-msg').innerHTML = ` + + Anticipated Commissioning date has to be after Anticipated Construction start date + + `; } - request('finance-overview/', { - method: 'PUT', - credentials: 'same-origin', - body: JSON.stringify(result), - headers: new Headers({ - 'Content-Type': 'application/json', - 'X-CSRFToken': Cookies.get('csrftoken') - }) - }).then(res => { - if (!res.payload.err) { - // Display response message. Add that loan options changed if fund was changed. Reset didFundChange to false. - responseMessage = 'Saved'; - if (didFundChange) { - responseMessage += ' Loan Options table is reloded.'; + else { + result = { + 'pro_forma_start_date': proFormaStartDate, + 'pro_forma_duration': proFormaDuration, + 'analysis_date': analysisDate, + 'fund': fund, + 'anticipated_construction_start_date': anticipatedConstructionStarDate, + 'anticipated_commissioning_date': anticipatedCommissioningDate, + 'anticipated_construction_period': anticipatedConstructionPeriod, + } + request('finance-overview/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(result), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then(res => { + if (!res.payload.err) { + // Display response message. Add that loan options changed if fund was changed. Reset didFundChange to false. + responseMessage = 'Saved'; + if (didFundChange) { + responseMessage += ' Loan Options table is reloded.'; + } + var resMsg = document.querySelector('#pro-forma-form-save-msg'); + resMsg.innerHTML = `${responseMessage}`; + /** + * delete-loan-options request to the backend to delete loan options for this building id if previosly stored. + * This ensures that the loan options table is up to date with the latest fund. Clear the loan options table + * and reload with new fund's default loan options. + */ + if (didFundChange) { + didFundChange = false; + request(`loan-options/?loans=delete-loan-options`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + }, + }).then(res => { + var tableBody = document.querySelector('#loan-options-table tbody'); + tableBody.innerHTML = ``; + }); + } + getLoanOptionsTable(); } - var resMsg = document.querySelector('#pro-forma-form-save-msg'); - resMsg.innerHTML = `${responseMessage}`; - /** - * delete-loan-options request to the backend to delete loan options for this building id if previosly stored. - * This ensures that the loan options table is up to date with the latest fund. Clear the loan options table - * and reload with new fund's default loan options. - */ - if (didFundChange) { - didFundChange = false; - request(`loan-options/?loans=delete-loan-options`, { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }).then(res => { - var tableBody = document.querySelector('#loan-options-table tbody'); - tableBody.innerHTML = ``; - }); + else { + var resMsg = document.querySelector('#pro-forma-form-save-msg'); + resMsg.innerHTML = `${res.payload.err}`; } - getLoanOptionsTable(); - } - else { - var resMsg = document.querySelector('#pro-forma-form-save-msg'); - resMsg.innerHTML = `${res.payload.err}`; - } - }); + }); + } } return false; } @@ -819,6 +830,11 @@ function deleteCashBalanceRow(row) { var result = confirm('ARE YOU SURE YOU WANT TO DELETE THIS ROW?'); if (result) { table = document.querySelector('#cash-balance-table'); + var rowCount = table.rows.length; + if (rowCount === 2) { + alert('YOU MUST HAVE ATLEAST ONE CASH BALANCE ENTRY!'); + return false; + } table.deleteRow(row); for (rowInd = 1; rowInd < table.rows.length; rowInd++) { row = table.rows.item(rowInd).cells; @@ -1291,6 +1307,11 @@ function deleteLoanOptionsRow(row) { table = document.querySelector('#loan-options-table'); var result = confirm("Are you sure you want to delete this row?"); if (result) { + var rowCount = table.rows.length; + if (rowCount === 2) { + alert('YOU NEED ATLEAST ONE LOAN OPTION!'); + return false; + } table.deleteRow(row); for (rowInd = 1; rowInd < table.rows.length; rowInd++) { row = table.rows.item(rowInd).cells; diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index 3028a45..2bfe28c 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -47,7 +47,15 @@ function costEstimationTableBody() { */ function deleteCostEstimationRow(rowIndex) { table = document.querySelector('#cost-estimation-table tbody'); - body.deleteRow(rowIndex-1); + var result = confirm('ARE YOU SURE YOU WANT TO DELETE THIS ROW?'); + if (result) { + var rowCount = table.rows.length; + if (rowCount === 1) { + alert('YOU NEED ATLEAST ONE ITEM WITH ITS COST ESTIMATION!'); + return false; + } + body.deleteRow(rowIndex-1); + } return false; } -- GitLab From 1835423f8711d7fef3072bdbf7a620c279bf6eef Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 14:57:23 -0400 Subject: [PATCH 6/8] Fix typo in financialInputs frontend. Reload new graph in preliminary finance with new scenario data. --- .../static/financialInputs/scripts/app.js | 10 +++++----- .../static/preliminaryFinance/scripts/app.js | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 5077e1d..d5ba66d 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -91,10 +91,10 @@ function billProjectionDatesForm(form) { var proFormaDuration = document.querySelector('#pro-forma-duration-input').value; var analysisDate = document.querySelector('#analysis-date-input').value; var fund = document.querySelector('#id_fund-select').value; - var anticipatedConstructionStarDate = document.querySelector('#anticipated-construction-start-date-input').value; + var anticipatedConstructionStartDate = document.querySelector('#anticipated-construction-start-date-input').value; var anticipatedCommissioningDate = document.querySelector('#anticipated-commissioning-date-input').value; var anticipatedConstructionPeriod = document.querySelector('#anticipated-construction-period-input').value; - startYear = anticipatedConstructionStarDate.split('-') + startYear = anticipatedConstructionStartDate.split('-') endDate = anticipatedCommissioningDate.split('-'); var anticipatedConstructionStartYear = startYear[0]; var anticipatedConstructionStartMonth = startYear[1]; @@ -102,7 +102,7 @@ function billProjectionDatesForm(form) { var anticipatedCommissioningStartYear = endDate[0]; var anticipatedCommissioningStartMonth = endDate[1]; var anticipatedCommissioningStartDay = endDate[2]; - var anticipatedConstructionStarDateDict = { + var anticipatedConstructionStartDateDict = { 'day': anticipatedConstructionStartDay, 'month': anticipatedConstructionStartMonth, 'year': anticipatedConstructionStartYear, @@ -119,7 +119,7 @@ function billProjectionDatesForm(form) { } if (isProFormaDateCorrect) { // Ensure that commissioning date is after start date. - var validDate = validateDate(anticipatedConstructionStarDateDict, anticipatedCommissioningDateDict); + var validDate = validateDate(anticipatedConstructionStartDateDict, anticipatedCommissioningDateDict); if (!validDate) { document.querySelector('#pro-forma-form-save-msg').innerHTML = ` @@ -133,7 +133,7 @@ function billProjectionDatesForm(form) { 'pro_forma_duration': proFormaDuration, 'analysis_date': analysisDate, 'fund': fund, - 'anticipated_construction_start_date': anticipatedConstructionStarDate, + 'anticipated_construction_start_date': anticipatedConstructionStartDate, 'anticipated_commissioning_date': anticipatedCommissioningDate, 'anticipated_construction_period': anticipatedConstructionPeriod, } diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index 2bfe28c..f1de81d 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -270,8 +270,12 @@ function addPERow(body, economicsOverview) { * Load the Energy Expense Savings Projection stacked bar graph. */ function loadGraph(res) { - var graph = document.querySelector('#myChart'); - graph.innerHTML = ``; + var graph = document.querySelector('#savings-schedule'); + graph.innerHTML = ` +

+

+ + `; var config = { type: 'bar', data: { -- GitLab From 216942b0446059a490f84df8796feacee5c8cf10 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 15:28:25 -0400 Subject: [PATCH 7/8] Add comments. --- .../static/financialInputs/scripts/app.js | 23 ++++++++++++------- .../financialInputs/incomeStatement.html | 2 +- blocnote/apps/financialInputs/views.py | 3 ++- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index d5ba66d..fd36648 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -114,6 +114,11 @@ function billProjectionDatesForm(form) { } var proFormaStartYear = Number(proFormaStartDate.split('-')[0]); var isProFormaDateCorrect = true; + + /** + * Usually proforma start year is 3 years before current year. If the entered date is less than 3, confirm with + * user if that value is correct. + */ if (todaysDate.year - proFormaStartYear < 3) { isProFormaDateCorrect = confirm('ARE YOU SURE THE PROFORMA DATE IS CORRECT?'); } @@ -150,7 +155,7 @@ function billProjectionDatesForm(form) { // Display response message. Add that loan options changed if fund was changed. Reset didFundChange to false. responseMessage = 'Saved'; if (didFundChange) { - responseMessage += ' Loan Options table is reloded.'; + responseMessage += ' Loan Options table is reloaded.'; } var resMsg = document.querySelector('#pro-forma-form-save-msg'); resMsg.innerHTML = `${responseMessage}`; @@ -175,6 +180,7 @@ function billProjectionDatesForm(form) { getLoanOptionsTable(); } else { + // Display error message. var resMsg = document.querySelector('#pro-forma-form-save-msg'); resMsg.innerHTML = `${res.payload.err}`; } @@ -438,7 +444,7 @@ function createBillsOverviewRows(data) { function createCalculateButton() { text = `
- +
`; return text; @@ -448,7 +454,7 @@ function createCalculateButton() { function createSubmitButton() { text = `
- +
`; return text; @@ -511,6 +517,7 @@ function billsOverviewFormCalculate(data) { return false; } +// Watch bills overview table to display/erase error/success messages. var billsOverview = document.querySelector('#Energy-Bills-Overview'); billsOverview.onchange = function() { document.querySelector('#bills-overview-warning-message').innerHTML = ''; @@ -522,6 +529,7 @@ function billsOverviewFormSubmit(form) { for (const [key, value] of formData.entries()) { result[key] = value; } + // Check the estimation model. Since only rough estimation is implemented, allow only that. if (!(document.querySelector('#estimation-model').value === 'Rough Estimation')) { alert('Please recalculate the projection with rough estimation and try to save again'); return false; @@ -535,6 +543,7 @@ function billsOverviewFormSubmit(form) { 'X-CSRFToken': Cookies.get('csrftoken') }) }).then(res => { + // Display error/success message. var message = ''; if (!res.payload.err) { message = 'Saved'; @@ -545,6 +554,7 @@ function billsOverviewFormSubmit(form) { document.querySelector('#bills-overview-warning-message').innerHTML = ` ${message} `; + // Update the total once manual inputs are saved. var totalCharge = [] var billsOverviewTable = document.querySelector('#table-bills-overview tbody'); var columnLength = billsOverviewTable.rows[0].cells.length - 2; @@ -562,6 +572,7 @@ function billsOverviewFormSubmit(form) { return false; } +// Watch customer preference to display/erase error/success mesaages. var customerPreference = document.querySelector('#Customer-Preference'); customerPreference.onchange = function() { document.querySelector('#customer-preference-warning-message').innerHTML = ''; @@ -871,13 +882,9 @@ function addCashBalanceRow(balance, date, isFromBalanceSheet) { return false; } +// Watch cash balance to display/erase error/success messages. var cashBalanceForm = document.querySelector('#cash-balance-form'); cashBalanceForm.onchange = function() { - document.querySelector('cash-balance-message').innerHTML = ``; -} - -var cashBalance = document.querySelector('#cash-balance-form'); -cashBalance.onchange = function() { document.querySelector('#cash-balance-warning-message').innerHTML = ''; } diff --git a/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html b/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html index bbb9629..5b2d0c0 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html @@ -6,7 +6,7 @@
- +
diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 310c1d9..62984ec 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -4,7 +4,6 @@ import datetime from django.shortcuts import render from django.http import JsonResponse -from django.http import HttpResponseBadRequest from django.db import connections from django.views import View @@ -758,6 +757,8 @@ class IncomeStatementTable(View): now_year = int(datetime.datetime.now().year) result = {} prev_year = int(put[0]['year']) + + # Make sure the historical years are before current year, after proforma start year and in increasing order. for index in range(3): cur_year = int(put[index]['year']) if cur_year < pro_forma_year: -- GitLab From 2a0d715e859b5d58bcf3b36a080f78935212d3e3 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 18 May 2017 15:31:37 -0400 Subject: [PATCH 8/8] Add minor fixes. --- blocnote/apps/financialInputs/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 62984ec..904837b 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -45,7 +45,7 @@ def get_model_object(model, building_id): return 0 -def change_date_format(date): +def change_date_format(dates): """Change date format. The CSV file contains date in MM/DD/YYYY format. The database needs it @@ -57,7 +57,7 @@ def change_date_format(date): Returns: new_date: The date in YYYY-MM-DD format. """ - date_split = date.split('/') + date_split = dates.split('/') new_date = date_split[2] + '-' + date_split[0] + '-' + date_split[1] return new_date @@ -534,7 +534,6 @@ class BillsOverviewView(View): post = json.loads(request.body.decode()) self.model_bills_overview.objects.filter(building_id=building_id).delete() e_user, g_user, o_user, w_user = get_if_user_input(building_id) - financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) analysis_date = get_analysis_date(building_id) try: for i in range(analysis_date['proforma_duration']): -- GitLab