diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js
index 0bc6e05dff5e24e6e1ccad9e002c638783abeaec..eb5654bc8fa8617b135165b646f9ff996ecfec32 100644
--- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js
+++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js
@@ -59,24 +59,24 @@ function getBillProjectionDatesForm() {
headers: {
'Content-Type': 'application/json'
},
- }).then(res => {
- fundDropBox = document.querySelector('#id_fund-select');
- var funds = res.payload.result.funds;
- for (var fundIndex = 0; fundIndex < funds.length; fundIndex++) {
- fundDropBox.innerHTML += `
-
- `;
- if (res.payload.result.present) {
- document.querySelector('#pro-forma-date-input').value = res.payload.result.pro_forma_start_date;
- document.querySelector('#pro-forma-duration-input').value = res.payload.result.pro_forma_duration;
- document.querySelector('#analysis-date-input').value = res.payload.result.analysis_date;
- document.querySelector('#id_fund-select').value = res.payload.result.fund;
- document.querySelector('#anticipated-construction-start-date-input').value = res.payload.result.anticipated_construction_start_date;
- document.querySelector('#anticipated-commissioning-date-input').value = res.payload.result.anticipated_commissioning_date;
- document.querySelector('#anticipated-construction-period-input').value = res.payload.result.anticipated_construction_period;
- }
+ }).then(res => {
+ fundDropBox = document.querySelector('#id_fund-select');
+ var funds = res.payload.result.funds;
+ for (var fundIndex = 0; fundIndex < funds.length; fundIndex++) {
+ fundDropBox.innerHTML += `
+
+ `;
+ if (res.payload.result.present) {
+ document.querySelector('#pro-forma-date-input').value = res.payload.result.pro_forma_start_date;
+ document.querySelector('#pro-forma-duration-input').value = res.payload.result.pro_forma_duration;
+ document.querySelector('#analysis-date-input').value = res.payload.result.analysis_date;
+ document.querySelector('#id_fund-select').value = res.payload.result.fund;
+ document.querySelector('#anticipated-construction-start-date-input').value = res.payload.result.anticipated_construction_start_date;
+ document.querySelector('#anticipated-commissioning-date-input').value = res.payload.result.anticipated_commissioning_date;
+ document.querySelector('#anticipated-construction-period-input').value = res.payload.result.anticipated_construction_period;
}
- });
+ }
+ });
}
/**
@@ -564,7 +564,7 @@ function billsOverviewFormSubmit(form) {
var columnLength = billsOverviewTable.rows[0].cells.length - 2;
for (var cellIndex = 2; cellIndex < columnLength; cellIndex++) {
val = 0;
- for(var rowIndex = 0; rowIndex < 4; rowIndex++) {
+ for (var rowIndex = 0; rowIndex < 4; rowIndex++) {
val += Number(billsOverviewTable.rows[rowIndex].cells[cellIndex].children[0].value);
}
totalCharge.push(val);
@@ -1110,7 +1110,7 @@ function getIncomeStatementTable() {
},
}).then(res => {
loadIncomeStatementTable();
- if(res.payload.instance.present) {
+ if(Object.keys(res.payload.instance).length !== 0) {
recordList = res.payload.instance.result;
heading = document.querySelector('#income-statement-table-head');
body = document.querySelector('#income-statement-table-body');
@@ -1176,7 +1176,7 @@ function onCalculateIncomeStatement(data) {
'X-CSRFToken': Cookies.get('csrftoken')
})
}).then(res => {
- if (!res.payload.err) {
+ if (!res.err) {
const REVENUE_ROW = 0;
const UTILITY_EXPENSE_ROW = 1;
const ENERGY_EXPENSE_ROW = 2;
@@ -1190,8 +1190,8 @@ function onCalculateIncomeStatement(data) {
const TOTAL_EXPENSE_ROW = 10;
const NET_OPERATING_EXPENSE = 11;
var incomeStatementFull = res.payload.instance;
+ cellIndex = 1
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);
@@ -1201,6 +1201,7 @@ function onCalculateIncomeStatement(data) {
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);
+ cellIndex += 1;
}
var future = res.payload.future;
const FUTURE_INDEX = 4;
@@ -1235,15 +1236,17 @@ function onCalculateIncomeStatement(data) {
updateIncomeStatementTable(NET_OPERATING_EXPENSE,AVERAGE_DICT_INDEX,averageDict.noi);
var box = document.querySelector('#growth-rate option');
- box.innerHTML = `CAGR ${res.payload.CAGR}`;
+ 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}
- `;
+ res.err.responseBody.then((error) => {
+ document.querySelector('#income-statement-error-msg').innerHTML = `
+ ${error.error}
+ `;
+ })
}
})
return false;
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/index.html b/blocnote/apps/financialInputs/templates/financialInputs/index.html
index a208ffd677a8cd67f3633d461e4d272e2ac62027..6367e08e385b0bcb8b86da007d95e5e8ad932ca2 100644
--- a/blocnote/apps/financialInputs/templates/financialInputs/index.html
+++ b/blocnote/apps/financialInputs/templates/financialInputs/index.html
@@ -52,7 +52,6 @@
{% endblock %}
{% block scripts %}
-
{% endblock %}
diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py
index 3d69d6be5ebe999f2e9da49a50744eebb6fef1a4..cd60eea8cf5eacefa23da9c783c670742528cbf7 100644
--- a/blocnote/apps/financialInputs/views.py
+++ b/blocnote/apps/financialInputs/views.py
@@ -7,9 +7,8 @@ from django.http import JsonResponse
from django.db import connections
from django.views import View
-from bpfin.financials.financial_lib import organize_bill_overview
-from bpfin.financials.financial_lib import Income_Statement_Table
-from bpfin.lib.back_end_call import monthly_bill
+from bpfin.lib.back_end_call import monthly_bill, prior_income_statement_table
+
from .models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm
from .models import Liabilities, CashBalance, IncomeStatement, LoanOptions, DefaultLoan, Lender, GrowthRate
@@ -61,6 +60,47 @@ def change_date_format(dates):
return new_date
+def get_raw_bill(building_id):
+ """Fetch all the bills in the database.
+
+ Fetch the raw bill from the database and return them as a dictionary for each utility. The return value is
+ a dictionary with utility type as key and the bill as value.
+
+ Args:
+ building_id: id of the building.
+
+ Returns:
+ raw_bill: Dictionary with key as a utility type and value as the bill. The bill in turn is a dictionary
+ with keys as bill date from, bill date to, energy usage and charge.
+ """
+ utility_objs = {}
+ UTILITIES = ['electricity', 'gas', 'oil', 'water']
+ raw_bill = {}
+ for util in UTILITIES:
+ objs = Bills.objects.filter(
+ building_id=building_id,
+ utility_type=util,
+ )
+ utility_objs[util] = objs
+ if objs:
+ date_from = []
+ date_to = []
+ usage = []
+ charge = []
+ for obj in objs:
+ date_from.append(obj.date_from)
+ date_to.append(obj.date_to)
+ usage.append(float(obj.usage))
+ charge.append(float(obj.charge))
+ raw_bill[util] = {
+ 'date_from': date_from,
+ 'date_to': date_to,
+ 'usage': usage,
+ 'charge': charge,
+ }
+ return raw_bill
+
+
class Index(View):
"""Class that renders the index page."""
@@ -354,45 +394,6 @@ class BillsOverviewView(View):
model_bills = Bills
utility = ['electricity', 'gas', 'oil', 'water']
- def get_raw_bill(self, building_id):
- """Fetch all the bills in the database.
-
- Fetch the raw bill from the database and return them as a dictionary for each utility. The return value is
- a dictionary with utility type as key and the bill as value.
-
- Args:
- building_id: id of the building.
-
- Returns:
- raw_bill: Dictionary with key as a utility type and value as the bill. The bill in turn is a dictionary
- with keys as bill date from, bill date to, energy usage and charge.
- """
- utility_objs = {}
- raw_bill = {}
- for util in self.utility:
- objs = Bills.objects.filter(
- building_id=building_id,
- utility_type=util,
- )
- utility_objs[util] = objs
- if objs:
- date_from = []
- date_to = []
- usage = []
- charge = []
- for obj in objs:
- date_from.append(obj.date_from)
- date_to.append(obj.date_to)
- usage.append(float(obj.usage))
- charge.append(float(obj.charge))
- raw_bill[util] = {
- 'date_from': date_from,
- 'date_to': date_to,
- 'usage': usage,
- 'charge': charge,
- }
- return raw_bill
-
def get(self, request, building_id):
"""Handle HTTP GET request.
@@ -478,7 +479,7 @@ class BillsOverviewView(View):
analysis_date = get_analysis_date(building_id)
if put['Estimation Model'] == 'Rough Estimation':
# Fetch all bills from the database.
- raw_bill = self.get_raw_bill(building_id)
+ raw_bill = get_raw_bill(building_id)
# Project the charge for utilities whose atleast 12 months bills are available.
projected_bills = monthly_bill(raw_bill, analysis_date)
for util in self.utility:
@@ -819,10 +820,10 @@ class IncomeStatementTable(View):
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)
+ electricity[obj.year] = float(obj.electricity) if obj.electricity else None
+ gas[obj.year] = float(obj.gas) if obj.gas else None
+ oil[obj.year] = float(obj.oil) if obj.oil else None
+ water[obj.year] = float(obj.water) if obj.water else None
bill_overview = {
'electricity': [electricity, not obj.electricity_is_user_input],
'gas': [gas, not obj.gas_is_user_input],
@@ -841,7 +842,8 @@ class IncomeStatementTable(View):
building_id: id of the building.
"""
self.model.objects.filter(building_id=building_id).delete()
- for record in result:
+ for year in result:
+ record = result[year]
self.model.objects.create(
building_id=building_id,
year=record['year'],
@@ -851,6 +853,51 @@ class IncomeStatementTable(View):
non_utility_operating_expense=record['non_utility_expense']
)
+ def get_annual_bills(self, building_id):
+ """Fetch manually input annual values from the database."""
+ annual_bills = {}
+
+ electricity_objs = BillsOverview.objects.filter(
+ building_id=building_id,
+ electricity_is_user_input=True,
+ )
+
+ gas_objs = BillsOverview.objects.filter(
+ building_id=building_id,
+ gas_is_user_input=True,
+ )
+
+ oil_objs = BillsOverview.objects.filter(
+ building_id=building_id,
+ oil_is_user_input=True,
+ )
+
+ water_objs = BillsOverview.objects.filter(
+ building_id=building_id,
+ water_is_user_input=True,
+ )
+
+ if electricity_objs:
+ annual_bills['electricity'] = {}
+ for obj in electricity_objs:
+ annual_bills['electricity'][obj.year] = float(obj.electricity) if obj.electricity else None
+
+ if gas_objs:
+ annual_bills['gas'] = {}
+ for obj in gas_objs:
+ annual_bills['gas'][obj.year] = float(obj.gas) if obj.gas else None
+
+ if oil_objs:
+ annual_bills['oil'] = {}
+ for obj in oil_objs:
+ annual_bills['oil'][obj.year] = float(obj.oil) if obj.oil else None
+
+ if water_objs:
+ annual_bills['water'] = {}
+ for obj in water_objs:
+ annual_bills['water'][obj.year] = float(obj.water) if obj.water else None
+ return annual_bills
+
def put(self, request, building_id):
"""Handle HTTP PUT request.
@@ -872,13 +919,13 @@ class IncomeStatementTable(View):
try:
bill_overview = self.get_bills_overview_data(building_id)
except:
- return JsonResponse({'err': 'There are no bills projected for this building'})
+ return JsonResponse({'error': 'There are no bills projected for this building'}, status=400)
# Get the analysis date from the financing overview model.
try:
analysis_date = get_analysis_date(building_id)
except:
- return JsonResponse({'err': 'Please fill in the proforma information form at the top!'})
+ return JsonResponse({'error': 'Please fill in the proforma information form at the top!'}, status=400)
# Convert the response body to the format bpfin accepts.
try:
@@ -887,71 +934,65 @@ class IncomeStatementTable(View):
int(analysis_date['proforma_start'].year)
)
except ValueError as err:
- return JsonResponse({'err': err.args[0]})
+ return JsonResponse({'error': err.args[0]}, status=400)
+ # Update the growth rate.
GrowthRate.objects.filter(building_id=building_id).delete()
GrowthRate.objects.create(
building_id=building_id,
growth_rate=float("{0:.2f}".format(growth_rate)),
)
- # 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 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']))
- record['net_non_energy_opex'] = float("{0:.2f}".format(record['net_non_energy_opex']))
- result_dict['energy_opex'] = float("{0:.2f}".format(result_dict['energy_opex']))
- result_dict['other_utility'] = float("{0:.2f}".format(result_dict['other_utility']))
- result_dict['net_non_energy_opex'] = float("{0:.2f}".format(result_dict['net_non_energy_opex']))
- result_dict['revenue'] = float("{0:.2f}".format(result_dict['revenue']))
- result_dict['utility_expense'] = float("{0:.2f}".format(result_dict['utility_expense']))
- result_dict['non_utility_expense'] = float("{0:.2f}".format(result_dict['non_utility_expense']))
- result_dict['total_opex'] = float("{0:.2f}".format(result_dict['total_opex']))
- result_dict['noi'] = float("{0:.2f}".format(result_dict['noi']))
-
- average_dict['electricity_opex'] = float("{0:.2f}".format(average_dict['electricity_opex']))
- average_dict['gas_opex'] = float("{0:.2f}".format(average_dict['gas_opex']))
- average_dict['oil_opex'] = float("{0:.2f}".format(average_dict['oil_opex']))
- average_dict['water_opex'] = float("{0:.2f}".format(average_dict['water_opex']))
- average_dict['energy_opex'] = float("{0:.2f}".format(average_dict['energy_opex']))
- average_dict['other_utility'] = float("{0:.2f}".format(average_dict['other_utility']))
- average_dict['net_non_energy_opex'] = float("{0:.2f}".format(average_dict['net_non_energy_opex']))
- average_dict['revenue'] = float("{0:.2f}".format(average_dict['revenue']))
- average_dict['utility_expense'] = float("{0:.2f}".format(average_dict['utility_expense']))
- average_dict['non_utility_expense'] = float("{0:.2f}".format(average_dict['non_utility_expense']))
- average_dict['total_opex'] = float("{0:.2f}".format(average_dict['total_opex']))
- average_dict['noi'] = float("{0:.2f}".format(average_dict['noi']))
-
- cagr = ('= ' + "{0:.2f}".format(cagr * 100) + '%')
- return JsonResponse({'instance': result, 'future': result_dict, 'average': average_dict, 'CAGR': cagr})
+ # Fetch all the raw bills for the building.
+ raw_bill = get_raw_bill(building_id)
+
+ # Fetch annual bills i.e. manually input annual charge values.
+ annual_bills = self.get_annual_bills(building_id)
+
+ # Call bpfin function to obtain the full, one year in future and average income statements and the CAGR value.
+ result = prior_income_statement_table(
+ raw_income_statement_input, raw_bill,
+ annual_bills, analysis_date,
+ growth_rate)
+ full_income_statement = result[0]
+ future_income_statement = result[1]
+ average_income_statement = result[2]
+ cagr = result[3]
+
+ # Obtain only the historical income statement from the full statement. Also format it to have only 2 decimal
+ # places.
+ historical_income_statement = {}
+ count = 0
+ for year in full_income_statement:
+ if count >= 3:
+ break
+ record = full_income_statement[year]
+ for key in record:
+ record[key] = float("{0:.2f}".format(record[key]))
+ historical_income_statement[year] = record
+ count += 1
+
+ # Save the historical values in the database.
+ self.save(historical_income_statement, building_id)
+
+ # Format future income statement to have only 2 decimal places.
+ for key in future_income_statement:
+ future_income_statement[key] = float("{0:.2f}".format(future_income_statement[key]))
+
+ # Format average income statement to have only 2 decimal places.
+ for key in average_income_statement:
+ if average_income_statement[key]:
+ average_income_statement[key] = float("{0:.2f}".format(average_income_statement[key]))
+
+ # Format CAGR to be in percentage and have only 2 decimal places.
+ cagr = float("{0:.2f}".format(cagr * 100))
+
+ return JsonResponse({
+ 'instance': historical_income_statement,
+ 'future': future_income_statement,
+ 'average': average_income_statement,
+ 'CAGR': cagr,
+ })
def get(self, request, building_id):
"""Handle HTTP GET request.
@@ -969,7 +1010,6 @@ class IncomeStatementTable(View):
objs = self.model.objects.filter(building_id=building_id)
instance = {}
if objs:
- instance['present'] = True
result = []
temp_result = {}
for obj in objs: