diff --git a/.gitignore b/.gitignore index 32b1e5a16a36f3d59c0566fb584e5e9f6c6a9f23..171d7787629356c3ebf8817d272bbf6961df57e6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ node_modules # misc .idea .vscode/ +static/ diff --git a/blocnote/apps/financialInputs/migrations/0012_customerpreference.py b/blocnote/apps/financialInputs/migrations/0012_customerpreference.py new file mode 100644 index 0000000000000000000000000000000000000000..2b643cf26b5b284d6b6a1860f98f93a5f436a2bd --- /dev/null +++ b/blocnote/apps/financialInputs/migrations/0012_customerpreference.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.6 on 2017-04-19 20:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('financialInputs', '0011_auto_20170418_0232'), + ] + + operations = [ + 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)), + ], + ), + ] diff --git a/blocnote/apps/financialInputs/models.py b/blocnote/apps/financialInputs/models.py index b10647c3055533d0d0f66255ec4a9bd5b48c8e83..139bfb31abb126c3861ab4384d7e22ef8a989137 100644 --- a/blocnote/apps/financialInputs/models.py +++ b/blocnote/apps/financialInputs/models.py @@ -56,3 +56,12 @@ class BillsOverview(models.Model): oil_is_user_input = models.BooleanField(default=False) gas = models.DecimalField(max_digits=10, decimal_places=2) gas_is_user_input = models.BooleanField(default=False) + + +class CustomerPreference(models.Model): + """Store customer preferences on payment.""" + + building_id = models.IntegerField() + downpayment = models.DecimalField(max_digits=10, decimal_places=2) + expected_payback = models.DecimalField(max_digits=3, decimal_places=0) + expected_net_noi_dscr = models.DecimalField(max_digits=5, decimal_places=2) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 9f076c4b68a695e03d3bd55c485ab0ec93ead38e..cfc018772ee7c48ae402e85cb6aec8d297d382e2 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -3,15 +3,16 @@ for (var utility_index in utilities) { loadInitialBillsTable(utilities[utility_index]); } loadBillsOverview(); +getCustomerPreferenceTable(); +/** + * Handle submition of the header form. Validate commissioning date is greater + * than construction start date. Create result dictionary containing the form + * data and convert into JSON to send to the backend. Upon success, load the + * Bills overview table as the Pro Forma year changes the start year of the + * Bills overview table. + */ function billProjectionDatesForm(form) { - /* - * Handle submition of the header form. Validate commissioning date is greater - * than construction start date. Create result dictionary containing the form - * data and convert into JSON to send to the backend. Upon success, load the - * Bills overview table as the Pro Forma year changes the start year of the - * Bills overview table. - */ const formData = new FormData(form); var validDate = validateDate(formData); if (!validDate) { @@ -23,7 +24,7 @@ function billProjectionDatesForm(form) { result[key] = value; } request('finance-overview/', { - method: 'put', + method: 'PUT', credentials: 'same-origin', body: JSON.stringify(result), headers: new Headers({ @@ -37,8 +38,8 @@ function billProjectionDatesForm(form) { return false; } +/** Validate that commissioning date is after the construction start date. */ function validateDate(data) { - /* Validate that commissioning date is after the construction start date. */ var startDateYear = data.get('anticipated_construction_start_date_year'); var startDateMonth = data.get('anticipated_construction_start_date_month'); var startDateDay = data.get('anticipated_construction_start_date_day'); @@ -61,16 +62,16 @@ function validateDate(data) { return true; } +/** + * Load initial bills table given a utility. Send GET request to obtain + * bill information and call updateTable function to update the bills table + * with the data obtained from backend. + */ function loadInitialBillsTable(utility) { - /* - * Load initial bills table given a utility. Send GET request to obtain - * bill information and call updateTable function to update the bills table - * with the data obtained from backend. - */ const table = document.querySelector('#'+utility); request(`bills/?utility_type=${utility}`, { - method: 'get', + method: 'GET', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' @@ -83,17 +84,17 @@ function loadInitialBillsTable(utility) { }) } - +/** + * Update the Bills table with the data. Function queries to get the table + * component and then updates the component with the text. + */ function updateTable(utility, text) { - /* Update the Bills table with the data. Function queries to get the table - * component and then updates the component with the text. - */ table = document.querySelector('#'+utility); table.innerHTML = text; } +/** Generate html text for a given utility and bills data from backend. */ function getText(result, utility) { - /* Generate html text for a given utility and bills data from backend. */ var text = ` @@ -115,11 +116,12 @@ function getText(result, utility) { return text; } +/** + * Upload an energy bill. Get the id of the component to identify the + * utility type. Use reader to read as text and call the sendFile function + * to send the contents to the backend. + */ function uploadFile(data) { - /* Upload an energy bill. Get the id of the component to identify the - * utility type. Use reader to read as text and call the sendFile function - * to send the contents to the backend. - */ const id = document.getElementsByClassName("show")[0].getAttribute("id"); csvFile = data.files[0]; var reader = new FileReader(); @@ -133,10 +135,10 @@ function uploadFile(data) { return false; } +/** HTTP PUT request to upload the contents of the utility bills file */ function sendFile(id, content) { - /* HTTP PUT request to upload the contents of the utility bills file */ request(`bills/`, { - method: 'put', + method: 'PUT', credentials: 'same-origin', body: JSON.stringify(content), headers: new Headers({ @@ -150,23 +152,22 @@ function sendFile(id, content) { }); } +/** Create form tag Energy Bills Overview */ function startOverviewForm() { - /* Create form tag Energy Bills Overview */ var text = ``; return text; } +/** Create dropdown box to select estimation algorithm */ function estimationDropDownBox() { - /* Create dropdown box to select estimation algorithm */ var text = ``; return text; } - +/** Create table and column headings */ function createOverviewColumnHeaders(year) { - /* Create table and column headings */ var text = `
@@ -177,9 +178,8 @@ function createOverviewColumnHeaders(year) { `; return text; } - +/** Display if user input is needed or not */ function createFlag(state) { - /* Display if user input is needed or not */ if(state) { text = `Need User Input`; } @@ -189,11 +189,12 @@ function createFlag(state) { return text; } +/** + * Create input field. Add attribute readonly if value is grabbed from + * database. If value is not from database, make it a user input. Name + * the input along with utility type to uniquely identify it. + */ function createInput(id, state, value) { - /* Create input field. Add attribute readonly if value is grabbed from - * database. If value is not from database, make it a user input. Name - * the input along with utility type to uniquely identify it. - */ var text = ``; var is_readonly = ``; if (!state) { @@ -203,10 +204,12 @@ function createInput(id, state, value) { return text; } +/** + * Add rows to the energy bills table. Currently adds only one entry in + * each column. Will be modified to display entries for 20 years. + */ function createOverviewRows(data) { - /* Add rows to the energy bills table. Currently adds only one entry in - * each column. Will be modified to display entries for 20 years. - */ + var user_input = ''; var text = ``; for (var utility in utilities) { @@ -228,12 +231,13 @@ function createOverviewRows(data) { return text; } +/** + * Make HTTP GET request to get the bills overview information. Call all + * the relevant functions to create the energy overview table. + */ function loadBillsOverview() { - /* Make HTTP GET request to get the bills overview information. Call all - * the relevant functions to create the energy overview table. - */ request(`bills-overview/`, { - method: 'get', + method: 'GET', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' @@ -251,18 +255,19 @@ function loadBillsOverview() { }) } +/** + * Handle Submition of energy bills overview form. Get form data and create + * a dictionary result with the formdata. Send result to backend. This will + * be modified to receive all projected rows for ~20 years. + */ function billsOverviewFormSubmit(data) { - /* Handle Submition of energy bills overview form. Get form data and create - * a dictionary result with the formdata. Send result to backend. This will - * be modified to receive all projected rows for ~20 years. - */ const formData = new FormData(data); const result = {}; for (const [key, value] of formData.entries()) { result[key] = value; } request(`bills-overview/`, { - method: 'put', + method: 'PUT', credentials: 'same-origin', body: JSON.stringify(result), headers: new Headers({ @@ -272,3 +277,80 @@ function billsOverviewFormSubmit(data) { }); return false; } + +/** + * Handle customer preference form submit. Make a HTTP PUT request. + */ +function customerPreferenceForm(form) { + /* Handle customer preference form submission */ + formData = new FormData(form); + const result = {}; + for (const [key, value] of formData.entries()) { + result[key] = value; + } + request('customer-preference/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(result), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }); + return false; +} + +/** + * Create the customer preference table. Display values if previously filled else display defaults. + */ +function createCustomerPreferenceTable(instance) { + const customerPreferenceForm = document.querySelector('#Customer-Preference'); + var downpayment = 0; + var expectedPayback = 999; + var expectedNetNOIDSCR = 1.15 + + if (instance.status) { + var downpayment = instance['downpayment']; + var expectedPayback = instance['expected_payback']; + var expectedNetNOIDSCR = instance['expected_net_noi_dscr'] + } + var text = `
+ + + + + + + + + + + + + + + + + + + + +
PreferenceValue
Affordable Downpayment
Expected PaybackMonths
Expected Net NOI DSCR
+ ` + customerPreferenceForm.innerHTML = text; +} + +/** + * Make HTTP GET request to obtain customer preference table data if present. + */ +function getCustomerPreferenceTable() { + request(`customer-preference/`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + }, + }).then(res => { + createCustomerPreferenceTable(res.payload.instance); + }); +} diff --git a/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html b/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html new file mode 100644 index 0000000000000000000000000000000000000000..ec52556d2192ba158afadf777a1482e381300846 --- /dev/null +++ b/blocnote/apps/financialInputs/templates/financialInputs/customerPreference.html @@ -0,0 +1,6 @@ +

+ Customer Preference +

+ + + diff --git a/blocnote/apps/financialInputs/templates/financialInputs/index.html b/blocnote/apps/financialInputs/templates/financialInputs/index.html index f615ef0a6142def9c67e3e7de991e991339bc2a0..25a35b7f86cb464c6df236a10cc66dd4a95288b8 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/customerPreference.html" %} +
{% endblock %} {% block scripts %} diff --git a/blocnote/apps/financialInputs/urls.py b/blocnote/apps/financialInputs/urls.py index 7f66beda4aa2c7435872a1ebc970b32be905b464..72b7fbdf0574cdc3f8f53989fab4dc773ac15d5c 100644 --- a/blocnote/apps/financialInputs/urls.py +++ b/blocnote/apps/financialInputs/urls.py @@ -7,4 +7,5 @@ urlpatterns = [ url(r'^finance-overview/$', views.BlocNoteHeader.as_view(), name='header'), url(r'^bills/$', views.BillsTable.as_view(), name='bills'), url(r'^bills-overview/$', views.BillsOverviewView.as_view(), name='bills_overview'), + url(r'^customer-preference/$', views.CustomerPreferenceView.as_view(), name='customer_preference'), ] diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 0488f731b9637cc2a280bd0f83ef9d637aefa8ce..37ca0fd0e47eceb7f6cc6a147240c1adcfaae244 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -4,7 +4,7 @@ from django.http import JsonResponse from django.db import connections from django.views import View -from .models import FinancingOverview, Bills, BillsOverview +from .models import FinancingOverview, Bills, BillsOverview, CustomerPreference from .forms import BlocNoteHeaderForm @@ -451,3 +451,69 @@ class BillsOverviewView(View): new_instance = self.get_instance(building_id) return JsonResponse({'status': new_instance}) + + +class CustomerPreferenceView(View): + """Create and update customer preference table.""" + + model = CustomerPreference + + def put(self, request, building_id): + """Create/Update customer preference table. + + Create a customer preference table if not present for this building + else update existing table with form data received from frontend. + + Args: + request: HTTP PUT request. + building_id: id of the building. + + Returns: + JsonResponse: Return a status that says ok. + """ + put = json.loads(request.body.decode()) + obj = self.model.objects.filter(building_id=building_id) + if obj: + obj.update( + building_id=building_id, + downpayment=put['Affordable-Downpayment'], + expected_payback=put['Expected-Payback'], + expected_net_noi_dscr=put['Expected-NOI-DSCR'] + ) + else: + self.model.objects.create( + building_id=building_id, + downpayment=put['Affordable-Downpayment'], + expected_payback=put['Expected-Payback'], + expected_net_noi_dscr=put['Expected-NOI-DSCR'] + ) + return JsonResponse({'status': 'ok'}) + + def get(self, request, building_id): + """Fetch customer preference table. + + Check database if the customer preference table exists for this + building. If it does, return that data as a dictionary, else return + status saying false. + + Args: + request: HTTP GET request. + building_id: id of the building. + + Returns: + JsonResponse: Return instance as a JSON. Instance is a dictionary + that contains a status which is true of the table is + present, else says false. If table is present, + instance also contains the table values. + """ + obj = get_model_object(self.model, building_id) + instance = {} + if obj: + 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}) +