diff --git a/blocnote/apps/financialInputs/forms.py b/blocnote/apps/financialInputs/forms.py deleted file mode 100644 index 64f7084670ac2d08b4983f102bd429aa2592239e..0000000000000000000000000000000000000000 --- a/blocnote/apps/financialInputs/forms.py +++ /dev/null @@ -1,51 +0,0 @@ -import datetime - -from django.forms import ModelForm -from django import forms -from django.forms.extras import SelectDateWidget - -from .models import FinancingOverview - - -class BlocNoteHeaderForm(ModelForm): - """Header Form. - - Form that appears at the top of the page. This class specifies the fields - and sets the form field types for dates, sets the range of dates to display - and adds attributes to some of the fields. - """ - - years_to_display = range(datetime.datetime.now().year - 15, - datetime.datetime.now().year + 50) - anticipated_construction_start_date = forms.DateField( - widget=SelectDateWidget(years=years_to_display) - ) - anticipated_commissioning_date = forms.DateField( - widget=SelectDateWidget(years=years_to_display) - ) - pro_forma_start_date = forms.DateField( - widget=SelectDateWidget(years=years_to_display) - ) - analysis_date = forms.DateField( - widget=SelectDateWidget(years=years_to_display) - ) - - class Meta: - model = FinancingOverview - fields = ('anticipated_construction_start_date', - 'anticipated_commissioning_date', - 'anticipated_construction_period', - 'pro_forma_start_date', - 'pro_forma_duration', - 'analysis_date', - 'fund') - - def __init__(self, *args, **kwargs): - super(BlocNoteHeaderForm, self).__init__(*args, **kwargs) - for field in iter(self.fields): - self.fields[field].widget.attrs.update({ - 'class': 'form-control' - }) - self.fields['fund'].widget.attrs.update({ - 'class': 'custom-select' - }) diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 2b2d615b3870f68ad10ad915e9b3170329c73a01..cfea40d83a14dd0507bfd42cf5e8d5044c991fe1 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -8,6 +8,7 @@ const todaysDate = { for (var utility_index in utilities) { loadInitialBillsTable(utilities[utility_index]); } +getBillProjectionDatesForm(); loadBillsOverview(); getIncomeStatementTable(); getCustomerPreferenceTable(); @@ -15,11 +16,23 @@ getLiabilitiesTable(); getCashBalanceForm(); getLoanOptionsTable(); +/** + * proformaForm will watch the form and if it changes, it will remove the response message is it was displayed. + */ +var proformaForm = document.querySelector('#pro-forma-form'); +proformaForm.onchange = function() { + var resMsg = document.querySelector('#pro-forma-form-save-msg'); + resMsg.innerHTML = ``; +} + /** * The following 2 functions display a warning message saying if fund is changed, it will affect the loan options. * This message is displayed when the mouse is over the fund select box. */ var fund = document.querySelector('#id_fund'); +/** + * This is to watch if fund value is changed. If it did, it would affect loan options. + */ var didFundChange = false; fund.onmouseenter = function() { var errorDiv = document.querySelector('#show-error'); @@ -35,6 +48,37 @@ fund.onchange = function() { didFundChange = true; } +/** + * HTTP GET request to fetch the proforma form information from the database if preent. It would receive the list of + * funds along with. + */ +function getBillProjectionDatesForm() { + request(`finance-overview`, { + method: 'GET', + credentials: 'same-origin', + 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; + } + } + }); +} + /** * Handle submition of the header form. Validate commissioning date is greater * than construction start date. Create result dictionary containing the form @@ -43,31 +87,45 @@ fund.onchange = function() { * Bills overview table. */ function billProjectionDatesForm(form) { - const formData = new FormData(form); - var anticipatedConstructionStartYear = formData.get('anticipated_construction_start_date_year'); - var anticipatedConstructionStartMonth = formData.get('anticipated_construction_start_date_month'); - var anticipatedConstructionStartDay = formData.get('anticipated_construction_start_date_day'); - var anticipatedCommissioningStartYear = formData.get('anticipated_commissioning_date_year'); - var anticipatedCommissioningStartMonth = formData.get('anticipated_commissioning_date_month'); - var anticipatedCommissioningStartDay = formData.get('anticipated_commissioning_date_day'); - var anticipatedConstructionStarDate = { + var proFormaStartDate = document.querySelector('#pro-forma-date-input').value; + 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 anticipatedCommissioningDate = document.querySelector('#anticipated-commissioning-date-input').value; + var anticipatedConstructionPeriod = document.querySelector('#anticipated-construction-period-input').value; + startYear = anticipatedConstructionStarDate.split('-') + endDate = anticipatedCommissioningDate.split('-'); + var anticipatedConstructionStartYear = startYear[0]; + var anticipatedConstructionStartMonth = startYear[1]; + var anticipatedConstructionStartDay = startYear[2]; + var anticipatedCommissioningStartYear = endDate[0]; + var anticipatedCommissioningStartMonth = endDate[1]; + var anticipatedCommissioningStartDay = endDate[2]; + var anticipatedConstructionStarDateDict = { 'day': anticipatedConstructionStartDay, 'month': anticipatedConstructionStartMonth, 'year': anticipatedConstructionStartYear, } - var anticipatedCommissioningDate = { + var anticipatedCommissioningDateDict = { 'day': anticipatedCommissioningStartDay, 'month': anticipatedCommissioningStartMonth, 'year': anticipatedCommissioningStartYear, } - var validDate = validateDate(anticipatedConstructionStarDate, anticipatedCommissioningDate); + // 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"); } else { - const result = {}; - for (const [key, value] of formData.entries()) { - result[key] = value; + 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', @@ -78,6 +136,13 @@ 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.'; + } + 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 @@ -94,9 +159,9 @@ function billProjectionDatesForm(form) { }).then(res => { var tableBody = document.querySelector('#loan-options-table tbody'); tableBody.innerHTML = ``; - getLoanOptionsTable(); }); } + getLoanOptionsTable(); }); } return false; diff --git a/blocnote/apps/financialInputs/templates/financialInputs/index.html b/blocnote/apps/financialInputs/templates/financialInputs/index.html index 11d2565f02f74eadccf89eee1e5ee029050a0211..a208ffd677a8cd67f3633d461e4d272e2ac62027 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/index.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/index.html @@ -13,7 +13,7 @@
- {% include "financialInputs/headerForm.html" with form=header_form %} + {% include "financialInputs/proFormaForm.html" %}
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html b/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html new file mode 100644 index 0000000000000000000000000000000000000000..f3d71224eddeb63249bef928b267fc2269805acb --- /dev/null +++ b/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html @@ -0,0 +1,56 @@ +
+
+
+
+
+ Pro Forma Start date + +
+
+ Pro Forma Duration (years) + +
+
+
+
+
+
+ Analysis Date + +
+
+ Fund + +
+
+
+
+
+
+
+
+ Anticipated Construction Start Date + +
+
+ Anticipated Commissioning Date + +
+
+
+
+
+
+ Anticipated Construction Period (weeks) + +
+
+ +
+
+
+
+
+
+
diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index e7102f5059edf4fabca70189cce7377ca51fc9e9..dded474a2e86ed7181aa5b38cfcd2ffc518c1d12 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -1,16 +1,18 @@ import json +from datetime import date + from django.shortcuts import render from django.http import JsonResponse from django.db import connections from django.views import View -from datetime import date from bpfin.utilbills.bill_backend_call import bill_prior_proj_rough_annual from bpfin.financials.financial_lib import organize_bill_overview from bpfin.financials.financial_lib import Income_Statement_Table -from .models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm, Liabilities, CashBalance, IncomeStatement, LoanOptions, DefaultLoan, Lender, GrowthRate -from .forms import BlocNoteHeaderForm + +from .models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm +from .models import Liabilities, CashBalance, IncomeStatement, LoanOptions, DefaultLoan, Lender, GrowthRate def get_model_object(model, building_id): @@ -73,36 +75,6 @@ class Index(View): building = dict(zip(columns, row)) return building - def get_context_for_index(self, building_id, building): - """Return context for index page. - - Create the context for the index page. The index page should get the - building data(which is a dictionary) and the header form. The function - checks if a header form for the given building. If it does not exist, - it creates a new form else it takes the existing form. - - Args: - building_id: id of the building. - building: A dictionary containing the building data. - - Returns: - context: A dictionary with the building data and header form. - """ - financing_overview_model_obj = get_model_object( - self.model, building_id - ) - if financing_overview_model_obj: - header_form = BlocNoteHeaderForm( - instance=financing_overview_model_obj - ) - else: - header_form = BlocNoteHeaderForm() - context = { - 'building': building, - 'header_form': header_form, - } - return context - def get(self, request, building_id): """GET route for Index page. @@ -116,7 +88,9 @@ class Index(View): render: The index.html page with the context. """ building = self.get_building_data(building_id) - context = self.get_context_for_index(building_id, building) + context = { + 'building': building, + } return render(request, 'financialInputs/index.html', context=context) @@ -124,52 +98,84 @@ class BlocNoteHeader(View): """This class handles header creation and update.""" model = FinancingOverview - form_class = BlocNoteHeaderForm - def handle_form(self, put, form, building_id): + def handle_form(self, put, building_id): """Handle form submit. - Take the header form passed by the frontend and validates it. If it is - a valid form, it makes an entry in the database and returns the JSON - response to be sent to the frontend. + Take the header form passed by the frontend and make an entry in the database. Args: put: This is the data sent in the PUT request by frontend. - form: Header form with data passed by frontend. building_id: id of the building. - - Returns: - JsonResponse: A JSON response sent to the frontend. If success, it - sends the form data else it returns error. """ - financing_overview_obj = get_model_object(self.model, building_id) - if financing_overview_obj: - form = self.form_class(put, instance=financing_overview_obj) - else: - form.instance.building_id = building_id - if form.is_valid(): - header = form.save() - return JsonResponse({**form.data, 'id': header.id}) - return JsonResponse(form.errors, status=400) + self.model.objects.filter(building_id=building_id).delete() + self.model.objects.create( + building_id=building_id, + fund_id=put['fund'], + pro_forma_start_date=put['pro_forma_start_date'], + pro_forma_duration=put['pro_forma_duration'], + analysis_date=put['analysis_date'], + anticipated_construction_start_date=put['anticipated_construction_start_date'], + anticipated_commissioning_date=put['anticipated_commissioning_date'], + anticipated_construction_period=put['anticipated_construction_period'], + ) def put(self, request, building_id): """PUT route for header. - Handle the PUT request for the header form. It loads the JSON data - onto a form and handles the submition of the form. It sends a JSON - response to the frontend with form data or error status. + Handle the PUT request for the header form. Store all the data received into the database. If storing was + successfull, send success message else send a failure message. Args: request: HTTP PUT request. building_id: id of the building. Returns: - JsonResponse: If success, returns form data and if not, returns - error status as JSON. + JsonResponse: If success, returns a message saying successful else return message saying it failed. """ put = json.loads(request.body.decode()) - form = self.form_class(put) - return self.handle_form(put, form, building_id) + result = {} + try: + self.handle_form(put, building_id) + result['msg'] = 'The data was saved.' + except: + result['msg'] = 'Sorry, the data could not be saved.' + return JsonResponse(result) + + def get(self, request, building_id): + """HTTP GET request. + + Fetch the financing overview data from the database and send to the frontend. Send the list of lenders + regardless of whether a financing overview entry has been made or not. + + Args: + request: HTTP GET request. + building_id: id of the building. + + Returns: + JsonResponse: Result with the financing overview data. + """ + financing_overview_objs = self.model.objects.filter(building_id=building_id) + funds_obj = Fund.objects.all() + funds = [] + for fund_obj in funds_obj: + funds.append((fund_obj.id, fund_obj.Name)) + result = {} + result['present'] = False + if financing_overview_objs: + financing_overview_obj = financing_overview_objs[0] + result = { + 'present': True, + 'fund': financing_overview_obj.fund.id, + 'pro_forma_start_date': financing_overview_obj.pro_forma_start_date, + 'pro_forma_duration': financing_overview_obj.pro_forma_duration, + 'analysis_date': financing_overview_obj.analysis_date, + 'anticipated_construction_start_date': financing_overview_obj.anticipated_construction_start_date, + 'anticipated_commissioning_date': financing_overview_obj.anticipated_commissioning_date, + 'anticipated_construction_period': financing_overview_obj.anticipated_construction_period, + } + result['funds'] = funds + return JsonResponse({'result': result}) class BillsTable(View):