From 6d966185bcdecb71b5b0a5a90c8fb545cf2b7c44 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 27 Apr 2017 11:14:21 -0400 Subject: [PATCH 1/7] Implement income statement table. --- .../migrations/0001_initial.py | 16 +- .../migrations/0002_auto_20170427_1415.py | 23 ++ blocnote/apps/financialInputs/models.py | 8 + .../static/financialInputs/scripts/app.js | 231 ++++++++++++++++++ .../financialInputs/incomeStatement.html | 22 ++ .../templates/financialInputs/index.html | 3 + blocnote/apps/financialInputs/urls.py | 1 + blocnote/apps/financialInputs/views.py | 89 ++++++- 8 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 blocnote/apps/financialInputs/migrations/0002_auto_20170427_1415.py create mode 100644 blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html diff --git a/blocnote/apps/financialInputs/migrations/0001_initial.py b/blocnote/apps/financialInputs/migrations/0001_initial.py index f73d21e..b361359 100644 --- a/blocnote/apps/financialInputs/migrations/0001_initial.py +++ b/blocnote/apps/financialInputs/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.6 on 2017-04-26 14:28 +# Generated by Django 1.10.6 on 2017-04-26 23:34 from __future__ import unicode_literals from django.db import migrations, models @@ -92,6 +92,20 @@ class Migration(migrations.Migration): ('Name', models.CharField(max_length=200)), ], ), + migrations.CreateModel( + name='IncomeStatement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('year', models.DecimalField(decimal_places=0, max_digits=4)), + ('revenue', models.DecimalField(decimal_places=2, max_digits=10)), + ('utility_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ('other_utility_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ('non_utility_operating_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ('net_non_energy_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ('total_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ], + ), migrations.CreateModel( name='Liabilities', fields=[ diff --git a/blocnote/apps/financialInputs/migrations/0002_auto_20170427_1415.py b/blocnote/apps/financialInputs/migrations/0002_auto_20170427_1415.py new file mode 100644 index 0000000..9c9f036 --- /dev/null +++ b/blocnote/apps/financialInputs/migrations/0002_auto_20170427_1415.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.6 on 2017-04-27 14:15 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('financialInputs', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='incomestatement', + name='net_non_energy_expense', + ), + migrations.RemoveField( + model_name='incomestatement', + name='total_expense', + ), + ] diff --git a/blocnote/apps/financialInputs/models.py b/blocnote/apps/financialInputs/models.py index 426d097..6d6bd79 100644 --- a/blocnote/apps/financialInputs/models.py +++ b/blocnote/apps/financialInputs/models.py @@ -87,3 +87,11 @@ class CashBalance(models.Model): is_from_balance_sheet = models.BooleanField(default=False) balance_amount = models.DecimalField(max_digits=10, decimal_places=2) + +class IncomeStatement(models.Model): + building_id = models.IntegerField() + year = models.DecimalField(max_digits=4, decimal_places=0) + revenue = models.DecimalField(max_digits=10, decimal_places=2) + utility_expense = models.DecimalField(max_digits=10, decimal_places=2) + other_utility_expense = models.DecimalField(max_digits=10, decimal_places=2) + non_utility_operating_expense = models.DecimalField(max_digits=10, decimal_places=2) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 1408ebb..c42b171 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -4,6 +4,7 @@ for (var utility_index in utilities) { loadInitialBillsTable(utilities[utility_index]); } loadBillsOverview(); +getIncomeStatementTable(); getCustomerPreferenceTable(); getLiabilitiesTable(); getCashBalanceForm(); @@ -742,3 +743,233 @@ function createDateComponent(mon, day, yr, num) { return text; } + +function loadIncomeStatementTable() { + loadIncomeStatemenHeading(); + loadIncomeStatemenBody(); + loadIncomeStatementDropdownBox(''); +} + +function loadIncomeStatemenHeading() { + var heading = document.querySelector('#income-statement-table-head'); + heading.innerHTML = ` + + Year + + + + Next Year + Average + + `; +} + +function loadIncomeStatemenBody() { + var body = document.querySelector('#income-statement-table-body'); + body.innerHTML = ` + + Revenue + + + + + + + + Utility Expense + + + + + + + + Energy Expense + + + + + + + + Electricity Bill + + + + + + + + Gas Bill + + + + + + + + Oil Bill + + + + + + + + Water Bill + + + + + + + + Other Utility Expense + + + + + + + + Non-Utility Operating Expense + + + + + + + + Net Non-Energy Expense + + + + + + + + Total Expense + + + + + + + + Net Operating Expense + + + + + + + `; +} + +function loadIncomeStatementDropdownBox(cagr) { + dropdownBox = document.querySelector('#income-statement-dropdown-box'); + var text = ``; + text += ` + Please select Growth Rate + + `; + dropdownBox.innerHTML = text; +} + +function getIncomeStatementTable() { + request(`income-statement/`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + }, + }).then(res => { + loadIncomeStatementTable(); + if(res.payload.instance.present) { + recordList = res.payload.instance.result; + heading = document.querySelector('#income-statement-table-head'); + body = document.querySelector('#income-statement-table-body'); + for (var index in recordList) { + cellIndex = Number(index)+1; + heading.rows[0].cells[cellIndex].innerHTML = ` + + `; + updateIncomeStatementTable(0, cellIndex, ``); + updateIncomeStatementTable(1, cellIndex, ``); + updateIncomeStatementTable(8, cellIndex, ``); + } + } + }); +} + +function onCalculateIncomeStatement(data) { + var head = document.querySelector('#income-statement-table-head'); + var body = document.querySelector('#income-statement-table-body'); + var form = document.querySelector('#income-statement-form'); + var formData = new FormData(form); + var growthRate = formData.get('Growth Rate'); + var result = []; + for (var cellIndex = 1; cellIndex <= 3; cellIndex++) { + var record = {}; + record['year'] = head.rows[0].cells[cellIndex].children[0].value; + record['revenue'] = body.rows[0].cells[cellIndex].children[0].value; + record['utility-expense'] = body.rows[1].cells[cellIndex].children[0].value; + record['non-utility-operating-expense'] = body.rows[8].cells[cellIndex].children[0].value; + result.push(record); + } + result.push({'growth-rate': growthRate}); + request('income-statement/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(result), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then(res => { + var incomeStatementFull = res.payload.instance; + for (key in incomeStatementFull) { + cellIndex = Number(key)+1; + updateIncomeStatementTable(2, cellIndex, incomeStatementFull[key].energy_opex); + updateIncomeStatementTable(3, cellIndex, incomeStatementFull[key].electricity_opex); + updateIncomeStatementTable(4, cellIndex, incomeStatementFull[key].gas_opex); + updateIncomeStatementTable(5, cellIndex, incomeStatementFull[key].oil_opex); + updateIncomeStatementTable(6, cellIndex, incomeStatementFull[key].water_opex); + updateIncomeStatementTable(7, cellIndex, incomeStatementFull[key].other_utility); + updateIncomeStatementTable(9, cellIndex, incomeStatementFull[key].net_non_energy_opex); + updateIncomeStatementTable(10, cellIndex, incomeStatementFull[key].total_opex); + updateIncomeStatementTable(11, cellIndex, incomeStatementFull[key].noi); + } + var future = res.payload.future; + heading = document.querySelector('#income-statement-table-head'); + heading.rows[0].cells[4].innerHTML = future.year; + updateIncomeStatementTable(0,4,future.revenue); + updateIncomeStatementTable(1,4,future.utility_expense); + updateIncomeStatementTable(2,4,future.energy_opex); + updateIncomeStatementTable(3,4,future.electricity_opex); + updateIncomeStatementTable(4,4,future.gas_opex); + updateIncomeStatementTable(5,4,future.oil_opex); + updateIncomeStatementTable(6,4,future.water_opex); + updateIncomeStatementTable(7,4,future.other_utility); + updateIncomeStatementTable(8,4,future.non_utility_expense); + updateIncomeStatementTable(9,4,future.net_non_energy_opex); + updateIncomeStatementTable(10,4,future.total_opex); + updateIncomeStatementTable(11,4,future.noi); + }) + return false; +} + +function updateIncomeStatementTable(rowIndex, cellIndex, value) { + var body = document.querySelector('#income-statement-table-body'); + body.rows[rowIndex].cells[cellIndex].innerHTML = value; +} diff --git a/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html b/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html new file mode 100644 index 0000000..f1ce56c --- /dev/null +++ b/blocnote/apps/financialInputs/templates/financialInputs/incomeStatement.html @@ -0,0 +1,22 @@ +
+

Income Statements

+
+
+
+
+
+
+ +
+
+
+
+ + + + + +
+
+
+
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/index.html b/blocnote/apps/financialInputs/templates/financialInputs/index.html index 2280a00..b3d50c3 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/index.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/index.html @@ -22,6 +22,9 @@
{% include "financialInputs/billsOverview.html" %}
+
+ {% include "financialInputs/incomeStatement.html" %} +
{% include "financialInputs/cashBalance.html" %}
diff --git a/blocnote/apps/financialInputs/urls.py b/blocnote/apps/financialInputs/urls.py index 0a0786b..8856aed 100644 --- a/blocnote/apps/financialInputs/urls.py +++ b/blocnote/apps/financialInputs/urls.py @@ -10,4 +10,5 @@ urlpatterns = [ url(r'^customer-preference/$', views.CustomerPreferenceView.as_view(), name='customer_preference'), url(r'^liabilities/$', views.LiabilitiesTable.as_view(), name='liabilities'), url(r'^cash-balance/$', views.CashBalanceView.as_view(), name='cash_balance'), + url(r'^income-statement/$', views.IncomeStatementTable.as_view(), name='income_statement'), ] diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index f269dd4..8682082 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -7,8 +7,11 @@ from django.views import View from datetime import date from bpfin.utilbills.bill_backend_call import bill_prior_proj_rough_annual from bpfin.financials.cash_balance import cash_balance +from bpfin.financials.financial_lib import organize_bill_overview +from bpfin.financials.income_statement_form_hist import form_income_statement_hist +from bpfin.financials.income_statement_next import income_statement_next -from .models import FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm, Liabilities, CashBalance +from .models import FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm, Liabilities, CashBalance, IncomeStatement from .forms import BlocNoteHeaderForm @@ -719,3 +722,87 @@ class CashBalanceView(View): ) return JsonResponse({'instance': 'OK'}) + +class IncomeStatementTable(View): + model = IncomeStatement + + def convert_response_format(self, put): + 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']) + return result, growth_rate + + def get_bills_overview_data(self, building_id): + objs = BillsOverview.objects.filter(building_id=building_id) + bill_overview = {} + electricity = {} + gas = {} + oil = {} + water = {} + if objs: + for obj in objs: + electricity[obj.year] = float(obj.electricity) + gas[obj.year] = float(obj.gas) + oil[obj.year] = float(obj.oil) + water[obj.year] = float(obj.water) + bill_overview = { + 'electricity': [electricity, not obj.electricity_is_user_input], + 'gas': [gas, not obj.gas_is_user_input], + 'oil': [oil, not obj.oil_is_user_input], + 'water': [water, not obj.water_is_user_input], + } + return bill_overview + + def save(self, result, building_id): + self.model.objects.filter(building_id=building_id).delete() + for record in result: + print(record) + self.model.objects.create( + building_id=building_id, + year=record['year'], + revenue=record['revenue'], + utility_expense=record['utility_expense'], + other_utility_expense=record['other_utility'], + non_utility_operating_expense=record['non_utility_expense'] + ) + + def put(self, request, building_id): + put = json.loads(request.body.decode()) + raw_income_statement_input, growth_rate = self.convert_response_format(put) + bill_overview = self.get_bills_overview_data(building_id) + financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) + analysis_date = {} + analysis_date['proforma_start'] = financing_overview_obj.pro_forma_start_date + analysis_date['proforma_duration'] = financing_overview_obj.pro_forma_duration + bill_overview_organized = organize_bill_overview(bill_overview, analysis_date) + income_statement_full = form_income_statement_hist(raw_income_statement_input, bill_overview_organized) + result = [] + for year in income_statement_full: + result.append(income_statement_full[year]) + result_dict = income_statement_next(income_statement_full, bill_overview_organized, growth_rate) + self.save(result, building_id) + return JsonResponse({'instance': result, 'future': result_dict}) + + def get(self, request, building_id): + objs = self.model.objects.filter(building_id=building_id) + instance = {} + instance['present'] = False + if objs: + instance['present'] = True + result = [] + for obj in objs: + record = {} + record['year'] = obj.year + record['revenue'] = obj.revenue + record['utility_expense'] = obj.utility_expense + record['non_utility_operating_expense'] = obj.non_utility_operating_expense + result.append(record) + instance['result'] = result + return JsonResponse({'instance': instance}) -- GitLab From 97c0b2494aef85535152e221f8d2713a96e86fa5 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Thu, 27 Apr 2017 16:35:08 -0400 Subject: [PATCH 2/7] Add proper migration files. --- .../migrations/0001_initial.py | 111 ++++++++++++++++++ .../migrations/0002_incomestatement.py | 27 +++++ 2 files changed, 138 insertions(+) create mode 100644 blocnote/apps/financialInputs/migrations/0001_initial.py create mode 100644 blocnote/apps/financialInputs/migrations/0002_incomestatement.py diff --git a/blocnote/apps/financialInputs/migrations/0001_initial.py b/blocnote/apps/financialInputs/migrations/0001_initial.py new file mode 100644 index 0000000..a5df4f7 --- /dev/null +++ b/blocnote/apps/financialInputs/migrations/0001_initial.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.6 on 2017-04-27 19:42 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Bills', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('date_from', models.DateField()), + ('date_to', models.DateField()), + ('utility_type', models.CharField(max_length=11)), + ('usage', models.DecimalField(decimal_places=2, max_digits=10)), + ('charge', models.DecimalField(decimal_places=2, max_digits=10)), + ], + ), + migrations.CreateModel( + name='BillsOverview', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('year', models.DecimalField(decimal_places=0, max_digits=4)), + ('electricity', models.DecimalField(decimal_places=2, max_digits=10)), + ('electricity_is_user_input', models.BooleanField(default=False)), + ('water', models.DecimalField(decimal_places=2, max_digits=10)), + ('water_is_user_input', models.BooleanField(default=False)), + ('oil', models.DecimalField(decimal_places=2, max_digits=10)), + ('oil_is_user_input', models.BooleanField(default=False)), + ('gas', models.DecimalField(decimal_places=2, max_digits=10)), + ('gas_is_user_input', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='CashBalance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('statement_date', models.DateField()), + ('is_from_balance_sheet', models.BooleanField(default=False)), + ('balance_amount', models.DecimalField(decimal_places=2, max_digits=10)), + ], + ), + migrations.CreateModel( + name='CustomerPreference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('downpayment', models.DecimalField(decimal_places=2, max_digits=10)), + ('expected_payback', models.DecimalField(decimal_places=0, max_digits=3)), + ('expected_net_noi_dscr', models.DecimalField(decimal_places=2, max_digits=5)), + ], + ), + migrations.CreateModel( + name='EstimationAlgorithm', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('algorithm', models.CharField(max_length=16)), + ('building_id', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='FinancingOverview', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('required_noi_dscr', models.DecimalField(decimal_places=2, default=0, max_digits=5)), + ('requrired_cash_dscr', models.DecimalField(decimal_places=2, default=0, max_digits=5)), + ('pro_forma_start_date', models.DateField()), + ('pro_forma_duration', models.DecimalField(decimal_places=0, max_digits=2)), + ('analysis_date', models.DateField()), + ('anticipated_construction_start_date', models.DateField()), + ('anticipated_commissioning_date', models.DateField()), + ('anticipated_construction_period', models.DecimalField(decimal_places=0, max_digits=3)), + ], + ), + migrations.CreateModel( + name='Fund', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Name', models.CharField(max_length=200)), + ], + ), + migrations.CreateModel( + name='Liabilities', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('input_date', models.DateField()), + ('lender', models.CharField(max_length=150)), + ('monthly_service', models.DecimalField(decimal_places=2, max_digits=10)), + ('remaining_term', models.DecimalField(decimal_places=0, max_digits=3)), + ], + ), + migrations.AddField( + model_name='financingoverview', + name='fund', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='financialInputs.Fund'), + ), + ] diff --git a/blocnote/apps/financialInputs/migrations/0002_incomestatement.py b/blocnote/apps/financialInputs/migrations/0002_incomestatement.py new file mode 100644 index 0000000..0f9ce6a --- /dev/null +++ b/blocnote/apps/financialInputs/migrations/0002_incomestatement.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.6 on 2017-04-27 20:30 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('financialInputs', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='IncomeStatement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('building_id', models.IntegerField()), + ('year', models.DecimalField(decimal_places=0, max_digits=4)), + ('revenue', models.DecimalField(decimal_places=2, max_digits=10)), + ('utility_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ('other_utility_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ('non_utility_operating_expense', models.DecimalField(decimal_places=2, max_digits=10)), + ], + ), + ] -- GitLab From 237833ca690ff79b8c274e3cf98acb2bd1a87297 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 2 May 2017 11:40:53 -0400 Subject: [PATCH 3/7] Modify income statement class in views to use the income statement table class in bpfin. Update views and app.js to have comments. Update app.js to update the CAGR value once it is calculated. --- .../static/financialInputs/scripts/app.js | 52 ++++++++- blocnote/apps/financialInputs/views.py | 103 ++++++++++++++++-- 2 files changed, 143 insertions(+), 12 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 8c5220a..80f358c 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -750,14 +750,21 @@ function createDateComponent(mon, day, yr, num) { return text; } +/** + * Load income statement table. The column heading are displayed first along with the table body. The growth rate + * drop down box is displayed as well. + */ function loadIncomeStatementTable() { loadIncomeStatemenHeading(); loadIncomeStatemenBody(); loadIncomeStatementDropdownBox(''); } +/** + * Display column heading of the income statement table. + */ function loadIncomeStatemenHeading() { - var heading = document.querySelector('#income-statement-table-head'); + var heading = document.querySelector('#income-statement-table thead'); heading.innerHTML = ` Year @@ -770,8 +777,11 @@ function loadIncomeStatemenHeading() { `; } +/** + * Display body of the income statement table. + */ function loadIncomeStatemenBody() { - var body = document.querySelector('#income-statement-table-body'); + var body = document.querySelector('#income-statement-table tbody'); body.innerHTML = ` Revenue @@ -872,13 +882,17 @@ function loadIncomeStatemenBody() { `; } +/** + * Display the growth rate drop down box. This takes in a CAGR value if present. The CAGR value will be passed only + * when the calculate button is hit. + */ function loadIncomeStatementDropdownBox(cagr) { dropdownBox = document.querySelector('#income-statement-dropdown-box'); var text = ``; text += ` Please select Growth Rate `; - updateIncomeStatementTable(0, cellIndex, ``); - updateIncomeStatementTable(1, cellIndex, ``); - updateIncomeStatementTable(8, cellIndex, ``); + var revenue = recordList[index].revenue; + var utilityExpense = recordList[index].utility_expense; + var nonUtilityExpense = recordList[index].non_utility_operating_expense; + var revenueInput = ` + + `; + var utilityExpenseInput = ` + + `; + var nonUtilityExpenseInput = ` + + `; + updateIncomeStatementTable(0, cellIndex, revenueInput); + updateIncomeStatementTable(1, cellIndex, utilityExpenseInput); + updateIncomeStatementTable(8, cellIndex, nonUtilityExpenseInput); } } }); @@ -965,48 +977,62 @@ 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(2, cellIndex, incomeStatementFull[key].energy_opex); - updateIncomeStatementTable(3, cellIndex, incomeStatementFull[key].electricity_opex); - updateIncomeStatementTable(4, cellIndex, incomeStatementFull[key].gas_opex); - updateIncomeStatementTable(5, cellIndex, incomeStatementFull[key].oil_opex); - updateIncomeStatementTable(6, cellIndex, incomeStatementFull[key].water_opex); - updateIncomeStatementTable(7, cellIndex, incomeStatementFull[key].other_utility); - updateIncomeStatementTable(9, cellIndex, incomeStatementFull[key].net_non_energy_opex); - updateIncomeStatementTable(10, cellIndex, incomeStatementFull[key].total_opex); - updateIncomeStatementTable(11, cellIndex, incomeStatementFull[key].noi); + 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[4].innerHTML = future.year; - updateIncomeStatementTable(0,4,future.revenue); - updateIncomeStatementTable(1,4,future.utility_expense); - updateIncomeStatementTable(2,4,future.energy_opex); - updateIncomeStatementTable(3,4,future.electricity_opex); - updateIncomeStatementTable(4,4,future.gas_opex); - updateIncomeStatementTable(5,4,future.oil_opex); - updateIncomeStatementTable(6,4,future.water_opex); - updateIncomeStatementTable(7,4,future.other_utility); - updateIncomeStatementTable(8,4,future.non_utility_expense); - updateIncomeStatementTable(9,4,future.net_non_energy_opex); - updateIncomeStatementTable(10,4,future.total_opex); - updateIncomeStatementTable(11,4,future.noi); + 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; - updateIncomeStatementTable(0,5,averageDict.revenue); - updateIncomeStatementTable(1,5,averageDict.utility_expense); - updateIncomeStatementTable(2,5,averageDict.energy_opex); - updateIncomeStatementTable(3,5,averageDict.electricity_opex); - updateIncomeStatementTable(4,5,averageDict.gas_opex); - updateIncomeStatementTable(5,5,averageDict.oil_opex); - updateIncomeStatementTable(6,5,averageDict.water_opex); - updateIncomeStatementTable(7,5,averageDict.other_utility); - updateIncomeStatementTable(8,5,averageDict.non_utility_expense); - updateIncomeStatementTable(9,5,averageDict.net_non_energy_opex); - updateIncomeStatementTable(10,5,averageDict.total_opex); - updateIncomeStatementTable(11,5,averageDict.noi); + 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}`; -- GitLab From d23b9a9d9505952f54208eaf6abd080c47b5225c Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 2 May 2017 16:21:27 -0400 Subject: [PATCH 6/7] Update app.js to have row index constants. --- .../static/financialInputs/scripts/app.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index e80398f..83a9488 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -923,6 +923,9 @@ function getIncomeStatementTable() { recordList = res.payload.instance.result; heading = document.querySelector('#income-statement-table-head'); body = document.querySelector('#income-statement-table-body'); + const REVENUE_ROW = 0; + const UTILITY_EXPENSE_ROW = 1; + const NON_UTILITY_OPERATING_EXPENSE_ROW = 8; for (var index in recordList) { cellIndex = Number(index)+1; heading.rows[0].cells[cellIndex].innerHTML = ` @@ -940,9 +943,9 @@ function getIncomeStatementTable() { var nonUtilityExpenseInput = ` `; - updateIncomeStatementTable(0, cellIndex, revenueInput); - updateIncomeStatementTable(1, cellIndex, utilityExpenseInput); - updateIncomeStatementTable(8, cellIndex, nonUtilityExpenseInput); + updateIncomeStatementTable(REVENUE_ROW, cellIndex, revenueInput); + updateIncomeStatementTable(UTILITY_EXPENSE_ROW, cellIndex, utilityExpenseInput); + updateIncomeStatementTable(NON_UTILITY_OPERATING_EXPENSE_ROW, cellIndex, nonUtilityExpenseInput); } } }); -- GitLab From 4b3b49b3f8ed8dd7a25ad56f0e5464767f4e535e Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 2 May 2017 17:51:38 -0400 Subject: [PATCH 7/7] Add comments in the put function of income statement. --- blocnote/apps/financialInputs/views.py | 38 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index e529df6..67fc7bf 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -847,23 +847,49 @@ class IncomeStatementTable(View): results and average with average results. """ put = json.loads(request.body.decode()) + + # Convert the response body to the format bpfin accepts. raw_income_statement_input, growth_rate = self.convert_response_format(put) + + # 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 = {} - analysis_date['proforma_start'] = financing_overview_obj.pro_forma_start_date - analysis_date['proforma_duration'] = financing_overview_obj.pro_forma_duration + 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) + + # income statement table is an object that takes in the income statement input and bill_overview_organized. income_statement_table = Income_Statement_Table(raw_income_statement_input, bill_overview_organized) + + # income_statement_full is the list of records with historical income statement data. income_statement_full = income_statement_table.get_hist_table() + + # cagr is compound annual growth rate. cagr = income_statement_table.get_cagr() + + # project function calculates the income statement data for the next few years based on the growth rate. income_statement_table.project(growth_rate, analysis_date, bill_overview_organized) - result = [] - for year in income_statement_full: - result.append(income_statement_full[year]) + + # result stores the historical income statement data as a list. + result = [income_statement_full[year] for year in income_statement_full] + + # result_dict has the income statement data for the next year. result_dict = income_statement_table.get_single_year(income_statement_table.hist_end_year + 1) + + # average_dict has the income statement data for the 3 historical years and the next year projected. average_dict = income_statement_table.get_average() + + # save function stores the data in the database. self.save(result, building_id) + + # Change the float formatting to have only 2 decimal places. for record in result: record['energy_opex'] = float("{0:.2f}".format(record['energy_opex'])) record['other_utility'] = float("{0:.2f}".format(record['other_utility'])) -- GitLab