diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index 74ea41d226185d3ec1a58fbcbff140663999aa80..0f957c11673c1ecab955abd2890c778576010939 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -1,8 +1,3 @@ -costEstimationTableHead(); -costEstimationTableBody(); -savingEstimationTableHead(); -savingEstimationTableBody(); - /** * Return the Scenario name. */ @@ -11,6 +6,17 @@ function getScenarioName() { return docs.value; } +/** + * setScenarioName() Set the name of the scenario on the tab. + * + * @param {...} scenarioName - The name of the scenario. + * + */ +function setScenarioName(scenarioName) { + let docs = document.querySelector('a.nav-link.active input'); + docs.value = scenarioName; +} + /** * Add column headings to the Cost Estimation table. */ @@ -28,14 +34,14 @@ function costEstimationTableHead() { /** * Load the Cost Estimation table body. */ -function costEstimationTableBody() { +function costEstimationTableBody(item, cost) { const body = document.querySelector('#cost-estimation-table tbody'); const rowCount = body.rows.length; const row = body.insertRow(rowCount); let cell = row.insertCell(0); - cell.innerHTML = ''; + cell.innerHTML = ``; cell = row.insertCell(1); - cell.innerHTML = '$'; + cell.innerHTML = ``; cell = row.insertCell(2); cell.innerHTML = ''; return false; @@ -76,7 +82,7 @@ function savingEstimationTableHead() { /** * Load Savings Estimation Table body. */ -function savingEstimationTableBody() { +function savingEstimationTableBody(utilities) { const body = document.querySelector('#saving-estimation-table tbody'); let rowCount = body.rows.length; let row = body.insertRow(rowCount); @@ -84,18 +90,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Electricity'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -105,18 +111,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Gas'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -126,18 +132,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Oil'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -147,18 +153,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Water'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; return false; @@ -195,7 +201,7 @@ function submitScenario() { } record = { - 'utility_type': savingEstimationBody.rows[rowIndex].cells[0].innerHTML, + 'utility_type': savingEstimationBody.rows[rowIndex].cells[0].innerHTML.toLowerCase(), 'estimated_savings': savingEstimationBody.rows[rowIndex].cells[1].children[0].value, 'used_before_retrofit': beforeRetrofit, 'used_after_retrofit': afterRetrofit, @@ -231,8 +237,10 @@ function submitScenario() { 'X-CSRFToken': Cookies.get('csrftoken') }) }).then(res => { - loadProjectEconomicsTable(res); - loadGraph(res); + if (!res.err) { + loadProjectEconomicsTable(res.payload.economics_overview); + loadGraph(res); + } }); return false; } @@ -241,10 +249,9 @@ function submitScenario() { * Take the response as argument and display the project economics table. This is the summary of the priliminary * analysis that gives vital information about the financial health of the building. */ -function loadProjectEconomicsTable(res) { +function loadProjectEconomicsTable(economicsOverview) { const title = document.querySelector('#project-economics-title'); title.innerHTML = 'Project Economics'; - const economicsOverview = res.payload.economics_overview; const body = document.querySelector('#project-economics-table tbody'); addPERow(body, economicsOverview); } @@ -312,3 +319,66 @@ function loadGraph(res) { new Chart(ctx, config); } +/** + * getScenario() Do a GET request to the backend. If scenario previously stored, populate the cost and savings table + * as well as fill project economics and generate the savings schedule graph. If not, display new cost and savings form + * to be filled. + * + */ +function getScenario() { + request('scenario/', { + method: 'GET', + credentials: 'same-origin', + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then((res) => { + if (!res.err) { + if (Object.keys(res.payload.economics_overview).length > 0 ) { + loadProjectEconomicsTable(res.payload.economics_overview); + loadGraph(res); + } + costEstimationTableHead(); + if (Object.keys(res.payload.costs).length > 0) { + res.payload.costs.forEach((costItem) => { + costEstimationTableBody(costItem.cost_item, costItem.cost); + }); + } else { + costEstimationTableBody('', ''); + } + savingEstimationTableHead(); + let savings = {}; + if (Object.keys(res.payload.savings).length > 0) { + savings = res.payload.savings; + } else { + savings = { + 'electricity': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + 'gas': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + 'oil': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + 'water': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + }; + } + savingEstimationTableBody(savings); + setScenarioName(res.payload.name); + } + }); +} + +getScenario(); diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index d5c7800f0b372977294a46674e11bbaec778e836..9fb1a1457062b60c1604516781a0cc0f4e68fabc 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -82,6 +82,7 @@ class Scenarios(View): record['scenario_id'] = scenario_obj[0].id SavingsEstimation.objects.create(**record) + def put(self, request, building_id): """Handle HTTP PUT request. @@ -92,83 +93,12 @@ class Scenarios(View): Returns: Dictionary with data to display the graph. """ put = json.loads(request.body.decode()) - - # Fetch all relevant data from database. - financing_overview = get_financing_overview(building_id) - analysis_date = { - 'proforma_start': financing_overview['pro_forma_start_date'], - 'proforma_duration': financing_overview['pro_forma_duration'] - } - commission_date = financing_overview['anticipated_commissioning_date'] - utilities = ['electricity', 'gas', 'oil', 'water'] - prior_month_bill = {} - for utility in utilities: - bill = get_utility_bill(building_id, utility) - if bill: - prior_month_bill[utility] = bill - growth_rate = get_growth_rate(building_id) - raw_income_statement = get_income_statement(building_id) - cash_balance_input_dict = get_cash_balance(building_id) - liability_dictionary = get_liabilities(building_id) - loan_options = get_loan_options(building_id) - customer_preference = get_customer_preference(building_id) - annual_bills = get_annual_bills(building_id) - - # Extract data from the PUT request body. - # Calculate the total cost of items. - costs = [] - cost_list = put['cost'] - for record in cost_list: - costs.append(float(record['cost'])) - total_cost = sum(costs) - savings_percent = get_savings_percent(put['savings']) - - # The following 2 variables are hard coded for now. They will be updated at a later point. - full_saving_dict = { - 'electricity': None, - 'gas': None, - 'oil': None, - 'water': None, - } - req_dscr = { - 'req_noi_dscr': 1.15, - 'req_cash_dscr': 1.15, - 'req_saving_dscr': 1.10 - } - - # prelim_scenario returns the project economics which are a set of values determining the financial health - # of the building and graph dictionary which contains total expense, loan repayment and total net savings. - # The graph_dict will be plotted as a stacked bar graph in the frontend to visually see if savings can cover - # loan repayment and if there will be any savings left are loan repayment. - graph_dict, project_economics_overview = prelim_scenario( - prior_month_bill, - annual_bills, - raw_income_statement, - growth_rate, - liability_dictionary, - cash_balance_input_dict, - loan_options, - analysis_date, - commission_date, - total_cost, - savings_percent, - full_saving_dict, - req_dscr, - customer_preference, + project_economics_overview, energy_expense_list, total_loan_list, net_savings_list, year_list = prelim_analysis( + put['cost'], + put['savings'], + building_id ) - energy_expense = graph_dict['energy_expenses'] - total_loan = graph_dict['total_loan'] - net_savings = graph_dict['net_savings'] - energy_expense_list = [] - total_loan_list = [] - net_savings_list = [] - year_list = [] - for year in sorted(energy_expense): - year_list.append(year) - energy_expense_list.append(energy_expense[year]) - total_loan_list.append(total_loan[year]) - net_savings_list.append(net_savings[year]) economics_overview = { 'Estimated Cost': float("{0:.2f}".format(project_economics_overview['estimated_cost'])), 'Overall Saving': float("{0:.2f}".format(project_economics_overview['overall_saving'])), @@ -191,7 +121,7 @@ class Scenarios(View): 'min_cash_dscr': project_economics_overview['min_cash_dscr'], 'self_finance_amount': 0, # TODO: Hard-coded for now. Will be implemented soon. } - self.save_scenario(scenario_details, cost_list, put['savings']) + self.save_scenario(scenario_details, put['cost'], put['savings']) return JsonResponse({ 'year_list': year_list, 'energy_expense_list': energy_expense_list, @@ -199,3 +129,161 @@ class Scenarios(View): 'net_savings_list': net_savings_list, 'economics_overview': economics_overview, }) + + def get(self, request, building_id): + """HTTP GET request. + + Fetch the scenario, cost and savings information from the database and send to the frontend for viewing. This + will be updated in the future to fetch all scenarios from the database and send to the frontend. + + Args: + request: HTTP GET request. + building_id (integer): id of the building. + + Returns: + scenario_details (dictionary): Dictionary containing all the scenario details. + cost_estimation (dictionary): Dictionary with key as item name and value as the cost estimation. + savings (dictionary): Dictionary with utility type and savings in decimal. + """ + project_economics_fields = { + 'estimated_cost': 'Estimated Cost', + 'overall_savings': 'Overall Savings', + 'first_year_savings': 'First Year Savings', + 'simple_payback': 'Simple Payback', + 'self_finance_amount': 'Self Finance Amount', + 'min_savings_dscr': 'Minimum Savings DSCR', + 'min_net_operating_income_dscr': 'Minimum Net Operating Income DSCR', + 'min_cash_dscr': 'Minimum Cash DSCR', + } + savings_fields = [ + 'estimated_savings', + 'used_before_retrofit', + 'used_after_retrofit', + ] + scenario_obj = Scenario.objects.filter(building_id=building_id) + project_economics = {} + costs = [] + savings = {} + savings_list = [] + scenario_name = '' + energy_expense_list = [] + total_loan_list = [] + net_savings_list = [] + year_list = [] + if scenario_obj: + scenario_name = scenario_obj[0].name + scenario_id = scenario_obj[0].id + for field in project_economics_fields: + name = project_economics_fields[field] + project_economics[name] = float(scenario_obj[0].__dict__[field]) + cost_obj = CostEstimation.objects.filter(scenario_id=scenario_id) + for cost in cost_obj: + cost_estimate = { + 'cost_item': cost.__dict__['item'], + 'cost': float(cost.__dict__['cost']), + } + costs.append(cost_estimate) + + savings_obj = SavingsEstimation.objects.filter(scenario_id=scenario_id) + for obj in savings_obj: + saving = {} + for field in savings_fields: + saving[field] = obj.__dict__[field] + saving['estimated_savings'] = float(saving['estimated_savings']) * 100 + savings[obj.__dict__['utility_type']] = saving + saving['utility_type'] = obj.__dict__['utility_type'] + savings_list.append(saving) + project_economics_overview, energy_expense_list, total_loan_list, net_savings_list, year_list = prelim_analysis( + costs, + savings_list, + building_id + ) + return JsonResponse({ + 'name': scenario_name, + 'economics_overview': project_economics, + 'costs': costs, + 'savings': savings, + 'energy_expense_list': energy_expense_list, + 'total_loan_list': total_loan_list, + 'net_savings_list': net_savings_list, + 'year_list': year_list, + }) + + +def prelim_analysis(cost, saving, building_id): + # Fetch all relevant data from database. + financing_overview = get_financing_overview(building_id) + analysis_date = { + 'proforma_start': financing_overview['pro_forma_start_date'], + 'proforma_duration': financing_overview['pro_forma_duration'] + } + commission_date = financing_overview['anticipated_commissioning_date'] + utilities = ['electricity', 'gas', 'oil', 'water'] + prior_month_bill = {} + for utility in utilities: + bill = get_utility_bill(building_id, utility) + if bill: + prior_month_bill[utility] = bill + growth_rate = get_growth_rate(building_id) + raw_income_statement = get_income_statement(building_id) + cash_balance_input_dict = get_cash_balance(building_id) + liability_dictionary = get_liabilities(building_id) + loan_options = get_loan_options(building_id) + customer_preference = get_customer_preference(building_id) + annual_bills = get_annual_bills(building_id) + + # Extract data from the PUT request body. + # Calculate the total cost of items. + costs = [] + cost_list = cost + for record in cost_list: + costs.append(float(record['cost'])) + total_cost = sum(costs) + savings_percent = get_savings_percent(saving) + + # The following 2 variables are hard coded for now. They will be updated at a later point. + full_saving_dict = { + 'electricity': None, + 'gas': None, + 'oil': None, + 'water': None, + } + req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10 + } + + # prelim_scenario returns the project economics which are a set of values determining the financial health + # of the building and graph dictionary which contains total expense, loan repayment and total net savings. + # The graph_dict will be plotted as a stacked bar graph in the frontend to visually see if savings can cover + # loan repayment and if there will be any savings left are loan repayment. + graph_dict, project_economics_overview = prelim_scenario( + prior_month_bill, + annual_bills, + raw_income_statement, + growth_rate, + liability_dictionary, + cash_balance_input_dict, + loan_options, + analysis_date, + commission_date, + total_cost, + savings_percent, + full_saving_dict, + req_dscr, + customer_preference, + ) + energy_expense = graph_dict['energy_expenses'] + total_loan = graph_dict['total_loan'] + net_savings = graph_dict['net_savings'] + energy_expense_list = [] + total_loan_list = [] + net_savings_list = [] + year_list = [] + for year in sorted(energy_expense): + year_list.append(year) + energy_expense_list.append(energy_expense[year]) + total_loan_list.append(total_loan[year]) + net_savings_list.append(net_savings[year]) + return project_economics_overview, energy_expense_list, total_loan_list, net_savings_list, year_list